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
/**
@@ -367,6 +379,9 @@ public function setRequest(RateRequest $request)
367
379
*/
368
380
public function getResult ()
369
381
{
382
+ if (!$ this ->_result ) {
383
+ $ this ->_result = $ this ->_trackFactory ->create ();
384
+ }
370
385
return $ this ->_result ;
371
386
}
372
387
@@ -1026,9 +1041,16 @@ protected function _getXMLTracking($tracking)
1026
1041
'AccountNumber ' => $ this ->getConfigData ('account ' ),
1027
1042
'MeterNumber ' => $ this ->getConfigData ('meter_number ' ),
1028
1043
],
1029
- 'Version ' => ['ServiceId ' => 'trck ' , 'Major ' => '5 ' , 'Intermediate ' => '0 ' , 'Minor ' => '0 ' ],
1030
- 'PackageIdentifier ' => ['Type ' => 'TRACKING_NUMBER_OR_DOORTAG ' , 'Value ' => $ tracking ],
1031
- 'IncludeDetailedScans ' => 1 ,
1044
+ 'Version ' => [
1045
+ 'ServiceId ' => 'trck ' ,
1046
+ 'Major ' => self ::$ trackServiceVersion ,
1047
+ 'Intermediate ' => '0 ' ,
1048
+ 'Minor ' => '0 ' ,
1049
+ ],
1050
+ 'SelectionDetails ' => [
1051
+ 'PackageIdentifier ' => ['Type ' => 'TRACKING_NUMBER_OR_DOORTAG ' , 'Value ' => $ tracking ],
1052
+ ],
1053
+ 'ProcessingOptions ' => 'INCLUDE_DETAILED_SCANS '
1032
1054
];
1033
1055
$ requestString = serialize ($ trackRequest );
1034
1056
$ response = $ this ->_getCachedQuotes ($ requestString );
@@ -1055,114 +1077,48 @@ protected function _getXMLTracking($tracking)
1055
1077
/**
1056
1078
* Parse tracking response
1057
1079
*
1058
- * @param string[] $trackingValue
1080
+ * @param string $trackingValue
1059
1081
* @param \stdClass $response
1060
1082
* @return void
1061
- * @SuppressWarnings(PHPMD.CyclomaticComplexity)
1062
- * @SuppressWarnings(PHPMD.NPathComplexity)
1063
- * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
1064
1083
*/
1065
1084
protected function _parseTrackingResponse ($ trackingValue , $ response )
1066
1085
{
1067
- if (is_object ($ response )) {
1068
- if ($ response ->HighestSeverity == 'FAILURE ' || $ response ->HighestSeverity == 'ERROR ' ) {
1069
- $ errorTitle = (string )$ response ->Notifications ->Message ;
1070
- } elseif (isset ($ response ->TrackDetails )) {
1071
- $ trackInfo = $ response ->TrackDetails ;
1072
- $ resultArray ['status ' ] = (string )$ trackInfo ->StatusDescription ;
1073
- $ resultArray ['service ' ] = (string )$ trackInfo ->ServiceInfo ;
1074
- $ timestamp = isset (
1075
- $ trackInfo ->EstimatedDeliveryTimestamp
1076
- ) ? $ trackInfo ->EstimatedDeliveryTimestamp : $ trackInfo ->ActualDeliveryTimestamp ;
1077
- $ timestamp = strtotime ((string )$ timestamp );
1078
- if ($ timestamp ) {
1079
- $ resultArray ['deliverydate ' ] = date ('Y-m-d ' , $ timestamp );
1080
- $ resultArray ['deliverytime ' ] = date ('H:i:s ' , $ timestamp );
1081
- }
1082
-
1083
- $ deliveryLocation = isset (
1084
- $ trackInfo ->EstimatedDeliveryAddress
1085
- ) ? $ trackInfo ->EstimatedDeliveryAddress : $ trackInfo ->ActualDeliveryAddress ;
1086
- $ deliveryLocationArray = [];
1087
- if (isset ($ deliveryLocation ->City )) {
1088
- $ deliveryLocationArray [] = (string )$ deliveryLocation ->City ;
1089
- }
1090
- if (isset ($ deliveryLocation ->StateOrProvinceCode )) {
1091
- $ deliveryLocationArray [] = (string )$ deliveryLocation ->StateOrProvinceCode ;
1092
- }
1093
- if (isset ($ deliveryLocation ->CountryCode )) {
1094
- $ deliveryLocationArray [] = (string )$ deliveryLocation ->CountryCode ;
1095
- }
1096
- if ($ deliveryLocationArray ) {
1097
- $ resultArray ['deliverylocation ' ] = implode (', ' , $ deliveryLocationArray );
1098
- }
1099
-
1100
- $ resultArray ['signedby ' ] = (string )$ trackInfo ->DeliverySignatureName ;
1101
- $ resultArray ['shippeddate ' ] = date ('Y-m-d ' , (int )$ trackInfo ->ShipTimestamp );
1102
- if (isset ($ trackInfo ->PackageWeight ) && isset ($ trackInfo ->Units )) {
1103
- $ weight = (string )$ trackInfo ->PackageWeight ;
1104
- $ unit = (string )$ trackInfo ->Units ;
1105
- $ resultArray ['weight ' ] = "{$ weight } {$ unit }" ;
1106
- }
1107
-
1108
- $ packageProgress = [];
1109
- if (isset ($ trackInfo ->Events )) {
1110
- $ events = $ trackInfo ->Events ;
1111
- if (isset ($ events ->Address )) {
1112
- $ events = [$ events ];
1113
- }
1114
- foreach ($ events as $ event ) {
1115
- $ tempArray = [];
1116
- $ tempArray ['activity ' ] = (string )$ event ->EventDescription ;
1117
- $ timestamp = strtotime ((string )$ event ->Timestamp );
1118
- if ($ timestamp ) {
1119
- $ tempArray ['deliverydate ' ] = date ('Y-m-d ' , $ timestamp );
1120
- $ tempArray ['deliverytime ' ] = date ('H:i:s ' , $ timestamp );
1121
- }
1122
- if (isset ($ event ->Address )) {
1123
- $ addressArray = [];
1124
- $ address = $ event ->Address ;
1125
- if (isset ($ address ->City )) {
1126
- $ addressArray [] = (string )$ address ->City ;
1127
- }
1128
- if (isset ($ address ->StateOrProvinceCode )) {
1129
- $ addressArray [] = (string )$ address ->StateOrProvinceCode ;
1130
- }
1131
- if (isset ($ address ->CountryCode )) {
1132
- $ addressArray [] = (string )$ address ->CountryCode ;
1133
- }
1134
- if ($ addressArray ) {
1135
- $ tempArray ['deliverylocation ' ] = implode (', ' , $ addressArray );
1136
- }
1137
- }
1138
- $ packageProgress [] = $ tempArray ;
1139
- }
1140
- }
1141
-
1142
- $ resultArray ['progressdetail ' ] = $ packageProgress ;
1143
- }
1086
+ if (!is_object ($ response ) || empty ($ response ->HighestSeverity )) {
1087
+ $ this ->appendTrackingError ($ trackingValue , __ ('Invalid response from carrier ' ));
1088
+ return ;
1089
+ } else if (in_array ($ response ->HighestSeverity , self ::$ trackingErrors )) {
1090
+ $ this ->appendTrackingError ($ trackingValue , (string ) $ response ->Notifications ->Message );
1091
+ return ;
1092
+ } else if (empty ($ response ->CompletedTrackDetails ) || empty ($ response ->CompletedTrackDetails ->TrackDetails )) {
1093
+ $ this ->appendTrackingError ($ trackingValue , __ ('No available tracking items ' ));
1094
+ return ;
1144
1095
}
1145
1096
1146
- if (!$ this ->_result ) {
1147
- $ this ->_result = $ this ->_trackFactory ->create ();
1097
+ $ trackInfo = $ response ->CompletedTrackDetails ->TrackDetails ;
1098
+
1099
+ // Fedex can return tracking details as single object instead array
1100
+ if (is_object ($ trackInfo )) {
1101
+ $ trackInfo = [$ trackInfo ];
1148
1102
}
1149
1103
1150
- if (isset ($ resultArray )) {
1104
+ $ result = $ this ->getResult ();
1105
+ $ carrierTitle = $ this ->getConfigData ('title ' );
1106
+ $ counter = 0 ;
1107
+ foreach ($ trackInfo as $ item ) {
1151
1108
$ tracking = $ this ->_trackStatusFactory ->create ();
1152
- $ tracking ->setCarrier (' fedex ' );
1153
- $ tracking ->setCarrierTitle ($ this -> getConfigData ( ' title ' ) );
1109
+ $ tracking ->setCarrier (self :: CODE );
1110
+ $ tracking ->setCarrierTitle ($ carrierTitle );
1154
1111
$ tracking ->setTracking ($ trackingValue );
1155
- $ tracking ->addData ($ resultArray );
1156
- $ this -> _result ->append ($ tracking );
1157
- } else {
1158
- $ error = $ this -> _trackErrorFactory -> create ();
1159
- $ error -> setCarrier ( ' fedex ' );
1160
- $ error -> setCarrierTitle ( $ this -> getConfigData ( ' title ' ));
1161
- $ error -> setTracking ( $ trackingValue );
1162
- $ error -> setErrorMessage (
1163
- $ errorTitle ? $ errorTitle : __ ('For some reason we can \'t retrieve tracking info right now. ' )
1112
+ $ tracking ->addData ($ this -> processTrackingDetails ( $ item ) );
1113
+ $ result ->append ($ tracking );
1114
+ $ counter ++;
1115
+ }
1116
+
1117
+ // no available tracking details
1118
+ if (! $ counter ) {
1119
+ $ this -> appendTrackingError (
1120
+ $ trackingValue , __ ('For some reason we can \'t retrieve tracking info right now. ' )
1164
1121
);
1165
- $ this ->_result ->append ($ error );
1166
1122
}
1167
1123
}
1168
1124
@@ -1586,4 +1542,169 @@ protected function filterDebugData($data)
1586
1542
}
1587
1543
return $ data ;
1588
1544
}
1545
+
1546
+ /**
1547
+ * Parse track details response from Fedex
1548
+ * @param \stdClass $trackInfo
1549
+ * @return array
1550
+ * @SuppressWarnings(PHPMD.CyclomaticComplexity)
1551
+ + @SuppressWarnings(PHPMD.NPathComplexity)
1552
+ */
1553
+ private function processTrackingDetails (\stdClass $ trackInfo )
1554
+ {
1555
+ $ result = [
1556
+ 'shippeddate ' => null ,
1557
+ 'deliverydate ' => null ,
1558
+ 'deliverytime ' => null ,
1559
+ 'deliverylocation ' => null ,
1560
+ 'weight ' => null ,
1561
+ 'progressdetail ' => [],
1562
+ ];
1563
+
1564
+ if (!empty ($ trackInfo ->ShipTimestamp )) {
1565
+ $ datetime = \DateTime::createFromFormat (\DateTime::ISO8601 , $ trackInfo ->ShipTimestamp );
1566
+ $ result ['shippeddate ' ] = $ datetime ->format ('Y-m-d ' );
1567
+ }
1568
+
1569
+ $ result ['signedby ' ] = !empty ($ trackInfo ->DeliverySignatureName ) ?
1570
+ (string ) $ trackInfo ->DeliverySignatureName :
1571
+ null ;
1572
+
1573
+ $ result ['status ' ] = (!empty ($ trackInfo ->StatusDetail ) && !empty ($ trackInfo ->StatusDetail ->Description )) ?
1574
+ (string ) $ trackInfo ->StatusDetail ->Description :
1575
+ null ;
1576
+ $ result ['service ' ] = (!empty ($ trackInfo ->Service ) && !empty ($ trackInfo ->Service ->Description )) ?
1577
+ (string ) $ trackInfo ->Service ->Description :
1578
+ null ;
1579
+
1580
+ $ datetime = $ this ->getDeliveryDateTime ($ trackInfo );
1581
+ if ($ datetime ) {
1582
+ $ result ['deliverydate ' ] = $ datetime ->format ('Y-m-d ' );
1583
+ $ result ['deliverytime ' ] = $ datetime ->format ('H:i:s ' );
1584
+ }
1585
+
1586
+ $ address = null ;
1587
+ if (!empty ($ trackInfo ->EstimatedDeliveryAddress )) {
1588
+ $ address = $ trackInfo ->EstimatedDeliveryAddress ;
1589
+ } elseif (!empty ($ trackInfo ->ActualDeliveryAddress )) {
1590
+ $ address = $ trackInfo ->ActualDeliveryAddress ;
1591
+ }
1592
+
1593
+ if (!empty ($ address )) {
1594
+ $ result ['deliverylocation ' ] = $ this ->getDeliveryAddress ($ address );
1595
+ }
1596
+
1597
+ if (!empty ($ trackInfo ->PackageWeight )) {
1598
+ $ result ['weight ' ] = sprintf (
1599
+ '%s %s ' ,
1600
+ (string ) $ trackInfo ->PackageWeight ->Value ,
1601
+ (string ) $ trackInfo ->PackageWeight ->Units
1602
+ );
1603
+ }
1604
+
1605
+ if (!empty ($ trackInfo ->Events )) {
1606
+ $ events = $ trackInfo ->Events ;
1607
+ if (is_object ($ events )) {
1608
+ $ events = [$ trackInfo ->Events ];
1609
+ }
1610
+ $ result ['progressdetail ' ] = $ this ->processTrackDetailsEvents ($ events );
1611
+ }
1612
+
1613
+ return $ result ;
1614
+ }
1615
+
1616
+ /**
1617
+ * Parse delivery datetime from tracking details
1618
+ * @param \stdClass $trackInfo
1619
+ * @return \Datetime|null
1620
+ */
1621
+ private function getDeliveryDateTime (\stdClass $ trackInfo )
1622
+ {
1623
+ $ timestamp = null ;
1624
+ if (!empty ($ trackInfo ->EstimatedDeliveryTimestamp )) {
1625
+ $ timestamp = $ trackInfo ->EstimatedDeliveryTimestamp ;
1626
+ } elseif (!empty ($ trackInfo ->ActualDeliveryTimestamp )) {
1627
+ $ timestamp = $ trackInfo ->ActualDeliveryTimestamp ;
1628
+ }
1629
+
1630
+ return $ timestamp ? \DateTime::createFromFormat (\DateTime::ISO8601 , $ timestamp ) : null ;
1631
+ }
1632
+
1633
+ /**
1634
+ * Get delivery address details in string representation
1635
+ * Return City, State, Country Code
1636
+ *
1637
+ * @param \stdClass $address
1638
+ * @return \Magento\Framework\Phrase|string
1639
+ */
1640
+ private function getDeliveryAddress (\stdClass $ address )
1641
+ {
1642
+ $ details = [];
1643
+
1644
+ if (!empty ($ address ->City )) {
1645
+ $ details [] = (string ) $ address ->City ;
1646
+ }
1647
+
1648
+ if (!empty ($ address ->StateOrProvinceCode )) {
1649
+ $ details [] = (string ) $ address ->StateOrProvinceCode ;
1650
+ }
1651
+
1652
+ if (!empty ($ address ->CountryCode )) {
1653
+ $ details [] = (string ) $ address ->CountryCode ;
1654
+ }
1655
+
1656
+ return implode (', ' , $ details );
1657
+ }
1658
+
1659
+ /**
1660
+ * Parse tracking details events from response
1661
+ * Return list of items in such format:
1662
+ * ['activity', 'deliverydate', 'deliverytime', 'deliverylocation']
1663
+ *
1664
+ * @param array $events
1665
+ * @return array
1666
+ */
1667
+ private function processTrackDetailsEvents (array $ events )
1668
+ {
1669
+ $ result = [];
1670
+ /** @var \stdClass $event */
1671
+ foreach ($ events as $ event ) {
1672
+ $ item = [
1673
+ 'activity ' => (string ) $ event ->EventDescription ,
1674
+ 'deliverydate ' => null ,
1675
+ 'deliverytime ' => null ,
1676
+ 'deliverylocation ' => null
1677
+ ];
1678
+
1679
+ if (!empty ($ event ->Timestamp )) {
1680
+ $ datetime = \DateTime::createFromFormat (\DateTime::ISO8601 , $ event ->Timestamp );
1681
+ $ item ['deliverydate ' ] = $ datetime ->format ('Y-m-d ' );
1682
+ $ item ['deliverytime ' ] = $ datetime ->format ('H:i:s ' );
1683
+ }
1684
+
1685
+ if (!empty ($ event ->Address )) {
1686
+ $ item ['deliverylocation ' ] = $ this ->getDeliveryAddress ($ event ->Address );
1687
+ }
1688
+
1689
+ $ result [] = $ item ;
1690
+ }
1691
+
1692
+ return $ result ;
1693
+ }
1694
+
1695
+ /**
1696
+ * Append error message to rate result instance
1697
+ * @param string $trackingValue
1698
+ * @param string $errorMessage
1699
+ */
1700
+ private function appendTrackingError ($ trackingValue , $ errorMessage )
1701
+ {
1702
+ $ error = $ this ->_trackErrorFactory ->create ();
1703
+ $ error ->setCarrier ('fedex ' );
1704
+ $ error ->setCarrierTitle ($ this ->getConfigData ('title ' ));
1705
+ $ error ->setTracking ($ trackingValue );
1706
+ $ error ->setErrorMessage ($ errorMessage );
1707
+ $ result = $ this ->getResult ();
1708
+ $ result ->append ($ error );
1709
+ }
1589
1710
}
0 commit comments