17
17
/**
18
18
* Fedex shipping implementation
19
19
*
20
- * @author Magento Core Team <core@magentocommerce.com>
20
+ * @author Magento Core Team <core@magentocommerce.com>
21
21
* @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
22
22
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
23
23
*/
@@ -126,6 +126,18 @@ class Carrier extends AbstractCarrierOnline implements \Magento\Shipping\Model\C
126
126
'Key ' , 'Password ' , 'MeterNumber '
127
127
];
128
128
129
+ /**
130
+ * Version of tracking service
131
+ * @var int
132
+ */
133
+ private static $ trackServiceVersion = 10 ;
134
+
135
+ /**
136
+ * List of TrackReply errors
137
+ * @var array
138
+ */
139
+ private static $ trackingErrors = ['FAILURE ' , 'ERROR ' ];
140
+
129
141
/**
130
142
* @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
131
143
* @param \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory
@@ -193,7 +205,7 @@ public function __construct(
193
205
$ wsdlBasePath = $ configReader ->getModuleDir (Dir::MODULE_ETC_DIR , 'Magento_Fedex ' ) . '/wsdl/ ' ;
194
206
$ this ->_shipServiceWsdl = $ wsdlBasePath . 'ShipService_v10.wsdl ' ;
195
207
$ this ->_rateServiceWsdl = $ wsdlBasePath . 'RateService_v10.wsdl ' ;
196
- $ this ->_trackServiceWsdl = $ wsdlBasePath . 'TrackService_v5 .wsdl ' ;
208
+ $ this ->_trackServiceWsdl = $ wsdlBasePath . 'TrackService_v ' . self :: $ trackServiceVersion . ' .wsdl ' ;
197
209
}
198
210
199
211
/**
@@ -371,6 +383,9 @@ public function setRequest(RateRequest $request)
371
383
*/
372
384
public function getResult ()
373
385
{
386
+ if (!$ this ->_result ) {
387
+ $ this ->_result = $ this ->_trackFactory ->create ();
388
+ }
374
389
return $ this ->_result ;
375
390
}
376
391
@@ -1034,9 +1049,16 @@ protected function _getXMLTracking($tracking)
1034
1049
'AccountNumber ' => $ this ->getConfigData ('account ' ),
1035
1050
'MeterNumber ' => $ this ->getConfigData ('meter_number ' ),
1036
1051
],
1037
- 'Version ' => ['ServiceId ' => 'trck ' , 'Major ' => '5 ' , 'Intermediate ' => '0 ' , 'Minor ' => '0 ' ],
1038
- 'PackageIdentifier ' => ['Type ' => 'TRACKING_NUMBER_OR_DOORTAG ' , 'Value ' => $ tracking ],
1039
- 'IncludeDetailedScans ' => 1 ,
1052
+ 'Version ' => [
1053
+ 'ServiceId ' => 'trck ' ,
1054
+ 'Major ' => self ::$ trackServiceVersion ,
1055
+ 'Intermediate ' => '0 ' ,
1056
+ 'Minor ' => '0 ' ,
1057
+ ],
1058
+ 'SelectionDetails ' => [
1059
+ 'PackageIdentifier ' => ['Type ' => 'TRACKING_NUMBER_OR_DOORTAG ' , 'Value ' => $ tracking ],
1060
+ ],
1061
+ 'ProcessingOptions ' => 'INCLUDE_DETAILED_SCANS '
1040
1062
];
1041
1063
$ requestString = serialize ($ trackRequest );
1042
1064
$ response = $ this ->_getCachedQuotes ($ requestString );
@@ -1063,114 +1085,48 @@ protected function _getXMLTracking($tracking)
1063
1085
/**
1064
1086
* Parse tracking response
1065
1087
*
1066
- * @param string[] $trackingValue
1088
+ * @param string $trackingValue
1067
1089
* @param \stdClass $response
1068
1090
* @return void
1069
- * @SuppressWarnings(PHPMD.CyclomaticComplexity)
1070
- * @SuppressWarnings(PHPMD.NPathComplexity)
1071
- * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
1072
1091
*/
1073
1092
protected function _parseTrackingResponse ($ trackingValue , $ response )
1074
1093
{
1075
- if (is_object ($ response )) {
1076
- if ($ response ->HighestSeverity == 'FAILURE ' || $ response ->HighestSeverity == 'ERROR ' ) {
1077
- $ errorTitle = (string )$ response ->Notifications ->Message ;
1078
- } elseif (isset ($ response ->TrackDetails )) {
1079
- $ trackInfo = $ response ->TrackDetails ;
1080
- $ resultArray ['status ' ] = (string )$ trackInfo ->StatusDescription ;
1081
- $ resultArray ['service ' ] = (string )$ trackInfo ->ServiceInfo ;
1082
- $ timestamp = isset (
1083
- $ trackInfo ->EstimatedDeliveryTimestamp
1084
- ) ? $ trackInfo ->EstimatedDeliveryTimestamp : $ trackInfo ->ActualDeliveryTimestamp ;
1085
- $ timestamp = strtotime ((string )$ timestamp );
1086
- if ($ timestamp ) {
1087
- $ resultArray ['deliverydate ' ] = date ('Y-m-d ' , $ timestamp );
1088
- $ resultArray ['deliverytime ' ] = date ('H:i:s ' , $ timestamp );
1089
- }
1090
-
1091
- $ deliveryLocation = isset (
1092
- $ trackInfo ->EstimatedDeliveryAddress
1093
- ) ? $ trackInfo ->EstimatedDeliveryAddress : $ trackInfo ->ActualDeliveryAddress ;
1094
- $ deliveryLocationArray = [];
1095
- if (isset ($ deliveryLocation ->City )) {
1096
- $ deliveryLocationArray [] = (string )$ deliveryLocation ->City ;
1097
- }
1098
- if (isset ($ deliveryLocation ->StateOrProvinceCode )) {
1099
- $ deliveryLocationArray [] = (string )$ deliveryLocation ->StateOrProvinceCode ;
1100
- }
1101
- if (isset ($ deliveryLocation ->CountryCode )) {
1102
- $ deliveryLocationArray [] = (string )$ deliveryLocation ->CountryCode ;
1103
- }
1104
- if ($ deliveryLocationArray ) {
1105
- $ resultArray ['deliverylocation ' ] = implode (', ' , $ deliveryLocationArray );
1106
- }
1107
-
1108
- $ resultArray ['signedby ' ] = (string )$ trackInfo ->DeliverySignatureName ;
1109
- $ resultArray ['shippeddate ' ] = date ('Y-m-d ' , (int )$ trackInfo ->ShipTimestamp );
1110
- if (isset ($ trackInfo ->PackageWeight ) && isset ($ trackInfo ->Units )) {
1111
- $ weight = (string )$ trackInfo ->PackageWeight ;
1112
- $ unit = (string )$ trackInfo ->Units ;
1113
- $ resultArray ['weight ' ] = "{$ weight } {$ unit }" ;
1114
- }
1115
-
1116
- $ packageProgress = [];
1117
- if (isset ($ trackInfo ->Events )) {
1118
- $ events = $ trackInfo ->Events ;
1119
- if (isset ($ events ->Address )) {
1120
- $ events = [$ events ];
1121
- }
1122
- foreach ($ events as $ event ) {
1123
- $ tempArray = [];
1124
- $ tempArray ['activity ' ] = (string )$ event ->EventDescription ;
1125
- $ timestamp = strtotime ((string )$ event ->Timestamp );
1126
- if ($ timestamp ) {
1127
- $ tempArray ['deliverydate ' ] = date ('Y-m-d ' , $ timestamp );
1128
- $ tempArray ['deliverytime ' ] = date ('H:i:s ' , $ timestamp );
1129
- }
1130
- if (isset ($ event ->Address )) {
1131
- $ addressArray = [];
1132
- $ address = $ event ->Address ;
1133
- if (isset ($ address ->City )) {
1134
- $ addressArray [] = (string )$ address ->City ;
1135
- }
1136
- if (isset ($ address ->StateOrProvinceCode )) {
1137
- $ addressArray [] = (string )$ address ->StateOrProvinceCode ;
1138
- }
1139
- if (isset ($ address ->CountryCode )) {
1140
- $ addressArray [] = (string )$ address ->CountryCode ;
1141
- }
1142
- if ($ addressArray ) {
1143
- $ tempArray ['deliverylocation ' ] = implode (', ' , $ addressArray );
1144
- }
1145
- }
1146
- $ packageProgress [] = $ tempArray ;
1147
- }
1148
- }
1149
-
1150
- $ resultArray ['progressdetail ' ] = $ packageProgress ;
1151
- }
1094
+ if (!is_object ($ response ) || empty ($ response ->HighestSeverity )) {
1095
+ $ this ->appendTrackingError ($ trackingValue , __ ('Invalid response from carrier ' ));
1096
+ return ;
1097
+ } else if (in_array ($ response ->HighestSeverity , self ::$ trackingErrors )) {
1098
+ $ this ->appendTrackingError ($ trackingValue , (string ) $ response ->Notifications ->Message );
1099
+ return ;
1100
+ } else if (empty ($ response ->CompletedTrackDetails ) || empty ($ response ->CompletedTrackDetails ->TrackDetails )) {
1101
+ $ this ->appendTrackingError ($ trackingValue , __ ('No available tracking items ' ));
1102
+ return ;
1152
1103
}
1153
1104
1154
- if (!$ this ->_result ) {
1155
- $ this ->_result = $ this ->_trackFactory ->create ();
1105
+ $ trackInfo = $ response ->CompletedTrackDetails ->TrackDetails ;
1106
+
1107
+ // Fedex can return tracking details as single object instead array
1108
+ if (is_object ($ trackInfo )) {
1109
+ $ trackInfo = [$ trackInfo ];
1156
1110
}
1157
1111
1158
- if (isset ($ resultArray )) {
1112
+ $ result = $ this ->getResult ();
1113
+ $ carrierTitle = $ this ->getConfigData ('title ' );
1114
+ $ counter = 0 ;
1115
+ foreach ($ trackInfo as $ item ) {
1159
1116
$ tracking = $ this ->_trackStatusFactory ->create ();
1160
- $ tracking ->setCarrier (' fedex ' );
1161
- $ tracking ->setCarrierTitle ($ this -> getConfigData ( ' title ' ) );
1117
+ $ tracking ->setCarrier (self :: CODE );
1118
+ $ tracking ->setCarrierTitle ($ carrierTitle );
1162
1119
$ tracking ->setTracking ($ trackingValue );
1163
- $ tracking ->addData ($ resultArray );
1164
- $ this -> _result ->append ($ tracking );
1165
- } else {
1166
- $ error = $ this -> _trackErrorFactory -> create ();
1167
- $ error -> setCarrier ( ' fedex ' );
1168
- $ error -> setCarrierTitle ( $ this -> getConfigData ( ' title ' ));
1169
- $ error -> setTracking ( $ trackingValue );
1170
- $ error -> setErrorMessage (
1171
- $ errorTitle ? $ errorTitle : __ ('For some reason we can \'t retrieve tracking info right now. ' )
1120
+ $ tracking ->addData ($ this -> processTrackingDetails ( $ item ) );
1121
+ $ result ->append ($ tracking );
1122
+ $ counter ++;
1123
+ }
1124
+
1125
+ // no available tracking details
1126
+ if (! $ counter ) {
1127
+ $ this -> appendTrackingError (
1128
+ $ trackingValue , __ ('For some reason we can \'t retrieve tracking info right now. ' )
1172
1129
);
1173
- $ this ->_result ->append ($ error );
1174
1130
}
1175
1131
}
1176
1132
@@ -1594,4 +1550,170 @@ protected function filterDebugData($data)
1594
1550
}
1595
1551
return $ data ;
1596
1552
}
1553
+
1554
+ /**
1555
+ * Parse track details response from Fedex
1556
+ * @param \stdClass $trackInfo
1557
+ * @return array
1558
+ * @SuppressWarnings(PHPMD.CyclomaticComplexity)
1559
+ + @SuppressWarnings(PHPMD.NPathComplexity)
1560
+ */
1561
+ private function processTrackingDetails (\stdClass $ trackInfo )
1562
+ {
1563
+ $ result = [
1564
+ 'shippeddate ' => null ,
1565
+ 'deliverydate ' => null ,
1566
+ 'deliverytime ' => null ,
1567
+ 'deliverylocation ' => null ,
1568
+ 'weight ' => null ,
1569
+ 'progressdetail ' => [],
1570
+ ];
1571
+
1572
+ if (!empty ($ trackInfo ->ShipTimestamp ) &&
1573
+ ($ datetime = \DateTime::createFromFormat (\DateTime::ISO8601 , $ trackInfo ->ShipTimestamp )) !== false
1574
+ ) {
1575
+ $ result ['shippeddate ' ] = $ datetime ->format ('Y-m-d ' );
1576
+ }
1577
+
1578
+ $ result ['signedby ' ] = !empty ($ trackInfo ->DeliverySignatureName ) ?
1579
+ (string ) $ trackInfo ->DeliverySignatureName :
1580
+ null ;
1581
+
1582
+ $ result ['status ' ] = (!empty ($ trackInfo ->StatusDetail ) && !empty ($ trackInfo ->StatusDetail ->Description )) ?
1583
+ (string ) $ trackInfo ->StatusDetail ->Description :
1584
+ null ;
1585
+ $ result ['service ' ] = (!empty ($ trackInfo ->Service ) && !empty ($ trackInfo ->Service ->Description )) ?
1586
+ (string ) $ trackInfo ->Service ->Description :
1587
+ null ;
1588
+
1589
+ $ datetime = $ this ->getDeliveryDateTime ($ trackInfo );
1590
+ if ($ datetime ) {
1591
+ $ result ['deliverydate ' ] = $ datetime ->format ('Y-m-d ' );
1592
+ $ result ['deliverytime ' ] = $ datetime ->format ('H:i:s ' );
1593
+ }
1594
+
1595
+ $ address = null ;
1596
+ if (!empty ($ trackInfo ->EstimatedDeliveryAddress )) {
1597
+ $ address = $ trackInfo ->EstimatedDeliveryAddress ;
1598
+ } elseif (!empty ($ trackInfo ->ActualDeliveryAddress )) {
1599
+ $ address = $ trackInfo ->ActualDeliveryAddress ;
1600
+ }
1601
+
1602
+ if (!empty ($ address )) {
1603
+ $ result ['deliverylocation ' ] = $ this ->getDeliveryAddress ($ address );
1604
+ }
1605
+
1606
+ if (!empty ($ trackInfo ->PackageWeight )) {
1607
+ $ result ['weight ' ] = sprintf (
1608
+ '%s %s ' ,
1609
+ (string ) $ trackInfo ->PackageWeight ->Value ,
1610
+ (string ) $ trackInfo ->PackageWeight ->Units
1611
+ );
1612
+ }
1613
+
1614
+ if (!empty ($ trackInfo ->Events )) {
1615
+ $ events = $ trackInfo ->Events ;
1616
+ if (is_object ($ events )) {
1617
+ $ events = [$ trackInfo ->Events ];
1618
+ }
1619
+ $ result ['progressdetail ' ] = $ this ->processTrackDetailsEvents ($ events );
1620
+ }
1621
+
1622
+ return $ result ;
1623
+ }
1624
+
1625
+ /**
1626
+ * Parse delivery datetime from tracking details
1627
+ * @param \stdClass $trackInfo
1628
+ * @return \Datetime|null
1629
+ */
1630
+ private function getDeliveryDateTime (\stdClass $ trackInfo )
1631
+ {
1632
+ $ timestamp = null ;
1633
+ if (!empty ($ trackInfo ->EstimatedDeliveryTimestamp )) {
1634
+ $ timestamp = $ trackInfo ->EstimatedDeliveryTimestamp ;
1635
+ } elseif (!empty ($ trackInfo ->ActualDeliveryTimestamp )) {
1636
+ $ timestamp = $ trackInfo ->ActualDeliveryTimestamp ;
1637
+ }
1638
+
1639
+ return $ timestamp ? \DateTime::createFromFormat (\DateTime::ISO8601 , $ timestamp ) : null ;
1640
+ }
1641
+
1642
+ /**
1643
+ * Get delivery address details in string representation
1644
+ * Return City, State, Country Code
1645
+ *
1646
+ * @param \stdClass $address
1647
+ * @return \Magento\Framework\Phrase|string
1648
+ */
1649
+ private function getDeliveryAddress (\stdClass $ address )
1650
+ {
1651
+ $ details = [];
1652
+
1653
+ if (!empty ($ address ->City )) {
1654
+ $ details [] = (string ) $ address ->City ;
1655
+ }
1656
+
1657
+ if (!empty ($ address ->StateOrProvinceCode )) {
1658
+ $ details [] = (string ) $ address ->StateOrProvinceCode ;
1659
+ }
1660
+
1661
+ if (!empty ($ address ->CountryCode )) {
1662
+ $ details [] = (string ) $ address ->CountryCode ;
1663
+ }
1664
+
1665
+ return implode (', ' , $ details );
1666
+ }
1667
+
1668
+ /**
1669
+ * Parse tracking details events from response
1670
+ * Return list of items in such format:
1671
+ * ['activity', 'deliverydate', 'deliverytime', 'deliverylocation']
1672
+ *
1673
+ * @param array $events
1674
+ * @return array
1675
+ */
1676
+ private function processTrackDetailsEvents (array $ events )
1677
+ {
1678
+ $ result = [];
1679
+ /** @var \stdClass $event */
1680
+ foreach ($ events as $ event ) {
1681
+ $ item = [
1682
+ 'activity ' => (string ) $ event ->EventDescription ,
1683
+ 'deliverydate ' => null ,
1684
+ 'deliverytime ' => null ,
1685
+ 'deliverylocation ' => null
1686
+ ];
1687
+
1688
+ if (!empty ($ event ->Timestamp )) {
1689
+ $ datetime = \DateTime::createFromFormat (\DateTime::ISO8601 , $ event ->Timestamp );
1690
+ $ item ['deliverydate ' ] = $ datetime ->format ('Y-m-d ' );
1691
+ $ item ['deliverytime ' ] = $ datetime ->format ('H:i:s ' );
1692
+ }
1693
+
1694
+ if (!empty ($ event ->Address )) {
1695
+ $ item ['deliverylocation ' ] = $ this ->getDeliveryAddress ($ event ->Address );
1696
+ }
1697
+
1698
+ $ result [] = $ item ;
1699
+ }
1700
+
1701
+ return $ result ;
1702
+ }
1703
+
1704
+ /**
1705
+ * Append error message to rate result instance
1706
+ * @param string $trackingValue
1707
+ * @param string $errorMessage
1708
+ */
1709
+ private function appendTrackingError ($ trackingValue , $ errorMessage )
1710
+ {
1711
+ $ error = $ this ->_trackErrorFactory ->create ();
1712
+ $ error ->setCarrier ('fedex ' );
1713
+ $ error ->setCarrierTitle ($ this ->getConfigData ('title ' ));
1714
+ $ error ->setTracking ($ trackingValue );
1715
+ $ error ->setErrorMessage ($ errorMessage );
1716
+ $ result = $ this ->getResult ();
1717
+ $ result ->append ($ error );
1718
+ }
1597
1719
}
0 commit comments