Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
Carrier.php
Go to the documentation of this file.
1 <?php
7 namespace Magento\Dhl\Model;
8 
19 
27 {
31  const DHL_CONTENT_TYPE_DOC = 'D';
38  const DIMENSION_MIN_CM = 3;
39  const DIMENSION_MIN_IN = 1;
45  const XML_PATH_EU_COUNTRIES_LIST = 'general/country/eu_countries';
46 
53 
57  const CODE = 'dhl';
58 
64  protected $_request;
65 
71  protected $_result;
72 
78  protected $_countryParams;
79 
85  protected $_errors = [];
86 
92  protected $_rates = [];
93 
99  protected $_code = self::CODE;
100 
106  protected $_freeMethod = 'free_method_nondoc';
107 
113  protected $_maxWeight = 70;
114 
120  protected $_isShippingLabelFlag = false;
121 
127  protected $_requestVariables = [
128  'id' => ['code' => 'dhl_id', 'setCode' => 'id'],
129  'password' => ['code' => 'dhl_password', 'setCode' => 'password'],
130  'account' => ['code' => 'dhl_account', 'setCode' => 'account_nbr'],
131  'shipping_key' => ['code' => 'dhl_shipping_key', 'setCode' => 'shipping_key'],
132  'shipping_intlkey' => ['code' => 'dhl_shipping_intl_key', 'setCode' => 'shipping_intl_key'],
133  'shipment_type' => ['code' => 'dhl_shipment_type', 'setCode' => 'shipment_type'],
134  'dutiable' => ['code' => 'dhl_dutiable', 'setCode' => 'dutiable'],
135  'dutypaymenttype' => ['code' => 'dhl_duty_payment_type', 'setCode' => 'duty_payment_type'],
136  'contentdesc' => ['code' => 'dhl_content_desc', 'setCode' => 'content_desc'],
137  ];
138 
144  protected $_isDomestic = false;
145 
151  protected $string;
152 
158  protected $_carrierHelper;
159 
163  protected $_coreDate;
164 
168  protected $_storeManager;
169 
173  protected $_configReader;
174 
178  protected $mathDivision;
179 
183  protected $readFactory;
184 
188  protected $_dateTime;
189 
194 
199  'SiteID', 'Password'
200  ];
201 
207  private $xmlValidator;
208 
238  public function __construct(
239  \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
240  \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory,
241  \Psr\Log\LoggerInterface $logger,
243  \Magento\Shipping\Model\Simplexml\ElementFactory $xmlElFactory,
244  \Magento\Shipping\Model\Rate\ResultFactory $rateFactory,
245  \Magento\Quote\Model\Quote\Address\RateResult\MethodFactory $rateMethodFactory,
246  \Magento\Shipping\Model\Tracking\ResultFactory $trackFactory,
247  \Magento\Shipping\Model\Tracking\Result\ErrorFactory $trackErrorFactory,
248  \Magento\Shipping\Model\Tracking\Result\StatusFactory $trackStatusFactory,
249  \Magento\Directory\Model\RegionFactory $regionFactory,
250  \Magento\Directory\Model\CountryFactory $countryFactory,
251  \Magento\Directory\Model\CurrencyFactory $currencyFactory,
252  \Magento\Directory\Helper\Data $directoryData,
253  \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry,
254  \Magento\Shipping\Helper\Carrier $carrierHelper,
255  \Magento\Framework\Stdlib\DateTime\DateTime $coreDate,
256  \Magento\Framework\Module\Dir\Reader $configReader,
257  \Magento\Store\Model\StoreManagerInterface $storeManager,
258  \Magento\Framework\Stdlib\StringUtils $string,
259  \Magento\Framework\Math\Division $mathDivision,
260  \Magento\Framework\Filesystem\Directory\ReadFactory $readFactory,
261  \Magento\Framework\Stdlib\DateTime $dateTime,
262  \Magento\Framework\HTTP\ZendClientFactory $httpClientFactory,
263  array $data = [],
264  \Magento\Dhl\Model\Validator\XmlValidator $xmlValidator = null
265  ) {
266  $this->readFactory = $readFactory;
267  $this->_carrierHelper = $carrierHelper;
268  $this->_coreDate = $coreDate;
269  $this->_storeManager = $storeManager;
270  $this->_configReader = $configReader;
271  $this->string = $string;
272  $this->mathDivision = $mathDivision;
273  $this->_dateTime = $dateTime;
274  $this->_httpClientFactory = $httpClientFactory;
275  parent::__construct(
276  $scopeConfig,
277  $rateErrorFactory,
278  $logger,
279  $xmlSecurity,
280  $xmlElFactory,
281  $rateFactory,
282  $rateMethodFactory,
283  $trackFactory,
284  $trackErrorFactory,
285  $trackStatusFactory,
286  $regionFactory,
287  $countryFactory,
288  $currencyFactory,
289  $directoryData,
291  $data
292  );
293  if ($this->getConfigData('content_type') == self::DHL_CONTENT_TYPE_DOC) {
294  $this->_freeMethod = 'free_method_doc';
295  }
296  $this->xmlValidator = $xmlValidator
297  ?: \Magento\Framework\App\ObjectManager::getInstance()->get(XmlValidator::class);
298  }
299 
307  protected function _getDefaultValue($origValue, $pathToValue)
308  {
309  if (!$origValue) {
310  $origValue = $this->_scopeConfig->getValue(
311  $pathToValue,
313  $this->getStore()
314  );
315  }
316 
317  return $origValue;
318  }
319 
327  {
328  if (!$this->canCollectRates()) {
329  return $this->getErrorMessage();
330  }
331 
332  $requestDhl = clone $request;
333  $this->setStore($requestDhl->getStoreId());
334 
335  $origCompanyName = $this->_getDefaultValue(
336  $requestDhl->getOrigCompanyName(),
338  );
339  $origCountryId = $this->_getDefaultValue($requestDhl->getOrigCountryId(), Shipment::XML_PATH_STORE_COUNTRY_ID);
340  $origState = $this->_getDefaultValue($requestDhl->getOrigState(), Shipment::XML_PATH_STORE_REGION_ID);
341  $origCity = $this->_getDefaultValue($requestDhl->getOrigCity(), Shipment::XML_PATH_STORE_CITY);
342  $origPostcode = $this->_getDefaultValue($requestDhl->getOrigPostcode(), Shipment::XML_PATH_STORE_ZIP);
343 
344  $requestDhl->setOrigCompanyName($origCompanyName)
345  ->setCountryId($origCountryId)
346  ->setOrigState($origState)
347  ->setOrigCity($origCity)
348  ->setOrigPostal($origPostcode);
349  $this->setRequest($requestDhl);
350 
351  $this->_result = $this->_getQuotes();
352  $this->_updateFreeMethodQuote($request);
353 
354  return $this->_result;
355  }
356 
363  protected function _setFreeMethodRequest($freeMethod)
364  {
365  $this->_rawRequest->setFreeMethodRequest(true);
366  $freeWeight = $this->getTotalNumOfBoxes($this->_rawRequest->getFreeMethodWeight());
367  $this->_rawRequest->setWeight($freeWeight);
368  $this->_rawRequest->setService($freeMethod);
369  }
370 
376  public function getResult()
377  {
378  return $this->_result;
379  }
380 
387  protected function _addParams(\Magento\Framework\DataObject $requestObject)
388  {
389  foreach ($this->_requestVariables as $code => $objectCode) {
390  if ($this->_request->getDhlId()) {
391  $value = $this->_request->getData($objectCode['code']);
392  } else {
393  $value = $this->getConfigData($code);
394  }
395  $requestObject->setData($objectCode['setCode'], $value);
396  }
397 
398  return $requestObject;
399  }
400 
409  public function setRequest(\Magento\Framework\DataObject $request)
410  {
411  $this->_request = $request;
412  $this->setStore($request->getStoreId());
413 
414  $requestObject = new \Magento\Framework\DataObject();
415 
416  $requestObject->setIsGenerateLabelReturn($request->getIsGenerateLabelReturn());
417 
418  $requestObject->setStoreId($request->getStoreId());
419 
420  if ($request->getLimitMethod()) {
421  $requestObject->setService($request->getLimitMethod());
422  }
423 
424  $requestObject = $this->_addParams($requestObject);
425 
426  if ($request->getDestPostcode()) {
427  $requestObject->setDestPostal($request->getDestPostcode());
428  }
429 
430  $requestObject->setOrigCountry(
431  $this->_getDefaultValue($request->getOrigCountry(), Shipment::XML_PATH_STORE_COUNTRY_ID)
432  )->setOrigCountryId(
433  $this->_getDefaultValue($request->getOrigCountryId(), Shipment::XML_PATH_STORE_COUNTRY_ID)
434  );
435 
436  $shippingWeight = $request->getPackageWeight();
437 
438  $requestObject->setValue(sprintf('%.2f', $request->getPackageValue()))
439  ->setValueWithDiscount($request->getPackageValueWithDiscount())
440  ->setCustomsValue($request->getPackageCustomsValue())
441  ->setDestStreet($this->string->substr(str_replace("\n", '', $request->getDestStreet()), 0, 35))
442  ->setDestStreetLine2($request->getDestStreetLine2())
443  ->setDestCity($request->getDestCity())
444  ->setOrigCompanyName($request->getOrigCompanyName())
445  ->setOrigCity($request->getOrigCity())
446  ->setOrigPhoneNumber($request->getOrigPhoneNumber())
447  ->setOrigPersonName($request->getOrigPersonName())
448  ->setOrigEmail(
449  $this->_scopeConfig->getValue(
450  'trans_email/ident_general/email',
452  $requestObject->getStoreId()
453  )
454  )
455  ->setOrigCity($request->getOrigCity())
456  ->setOrigPostal($request->getOrigPostal())
457  ->setOrigStreetLine2($request->getOrigStreetLine2())
458  ->setDestPhoneNumber($request->getDestPhoneNumber())
459  ->setDestPersonName($request->getDestPersonName())
460  ->setDestCompanyName($request->getDestCompanyName());
461 
462  $originStreet2 = $this->_scopeConfig->getValue(
465  $requestObject->getStoreId()
466  );
467 
468  $requestObject->setOrigStreet($request->getOrigStreet() ? $request->getOrigStreet() : $originStreet2);
469 
470  if (is_numeric($request->getOrigState())) {
471  $requestObject->setOrigState($this->_regionFactory->create()->load($request->getOrigState())->getCode());
472  } else {
473  $requestObject->setOrigState($request->getOrigState());
474  }
475 
476  if ($request->getDestCountryId()) {
477  $destCountry = $request->getDestCountryId();
478  } else {
479  $destCountry = self::USA_COUNTRY_ID;
480  }
481 
482  // for DHL, Puerto Rico state for US will assume as Puerto Rico country
483  // for Puerto Rico, dhl will ship as international
484  if ($destCountry == self::USA_COUNTRY_ID
485  && ($request->getDestPostcode() == '00912' || $request->getDestRegionCode() == self::PUERTORICO_COUNTRY_ID)
486  ) {
487  $destCountry = self::PUERTORICO_COUNTRY_ID;
488  }
489 
490  $requestObject->setDestCountryId($destCountry)
491  ->setDestState($request->getDestRegionCode())
492  ->setWeight($shippingWeight)
493  ->setFreeMethodWeight($request->getFreeMethodWeight())
494  ->setOrderShipment($request->getOrderShipment());
495 
496  if ($request->getPackageId()) {
497  $requestObject->setPackageId($request->getPackageId());
498  }
499 
500  $requestObject->setBaseSubtotalInclTax($request->getBaseSubtotalInclTax());
501 
502  $this->setRawRequest($requestObject);
503 
504  return $this;
505  }
506 
513  public function getAllowedMethods()
514  {
515  $contentType = $this->getConfigData('content_type');
516 
517  if ($this->_isDomestic) {
518  $allowedMethods = array_merge(
519  explode(',', $this->getConfigData('doc_methods')),
520  explode(',', $this->getConfigData('nondoc_methods'))
521  );
522  } else {
523  switch ($contentType) {
525  $allowedMethods = explode(',', $this->getConfigData('doc_methods'));
526  break;
528  $allowedMethods = explode(',', $this->getConfigData('nondoc_methods'));
529  break;
530  default:
531  throw new \Magento\Framework\Exception\LocalizedException(__('Wrong Content Type'));
532  }
533  }
534  $methods = [];
535  foreach ($allowedMethods as $method) {
536  $methods[$method] = $this->getDhlProductTitle($method);
537  }
538 
539  return $methods;
540  }
541 
549  public function getCode($type, $code = '')
550  {
551  $codes = [
552  'unit_of_measure' => ['L' => __('Pounds'), 'K' => __('Kilograms')],
553  'unit_of_dimension' => ['I' => __('Inches'), 'C' => __('Centimeters')],
554  'unit_of_dimension_cut' => ['I' => __('inch'), 'C' => __('cm')],
555  'dimensions' => ['HEIGHT' => __('Height'), 'DEPTH' => __('Depth'), 'WIDTH' => __('Width')],
556  'size' => ['0' => __('Regular'), '1' => __('Specific')],
557  'dimensions_variables' => [
560  'POUND' => \Zend_Measure_Weight::POUND,
563  'KILOGRAM' => \Zend_Measure_Weight::KILOGRAM,
566  'INCH' => \Zend_Measure_Length::INCH,
569  'CENTIMETER' => \Zend_Measure_Length::CENTIMETER,
570  ],
571  ];
572 
573  if (!isset($codes[$type])) {
574  return false;
575  } elseif ('' === $code) {
576  return $codes[$type];
577  }
578 
579  $code = strtoupper($code);
580  if (!isset($codes[$type][$code])) {
581  return false;
582  } else {
583  return $codes[$type][$code];
584  }
585  }
586 
593  public function getDhlProducts($doc)
594  {
595  $docType = [
596  '2' => __('Easy shop'),
597  '5' => __('Sprintline'),
598  '6' => __('Secureline'),
599  '7' => __('Express easy'),
600  '9' => __('Europack'),
601  'B' => __('Break bulk express'),
602  'C' => __('Medical express'),
603  'D' => __('Express worldwide'),
604  'U' => __('Express worldwide'),
605  'K' => __('Express 9:00'),
606  'L' => __('Express 10:30'),
607  'G' => __('Domestic economy select'),
608  'W' => __('Economy select'),
609  'I' => __('Domestic express 9:00'),
610  'N' => __('Domestic express'),
611  'O' => __('Others'),
612  'R' => __('Globalmail business'),
613  'S' => __('Same day'),
614  'T' => __('Express 12:00'),
615  'X' => __('Express envelope'),
616  ];
617 
618  $nonDocType = [
619  '1' => __('Domestic express 12:00'),
620  '3' => __('Easy shop'),
621  '4' => __('Jetline'),
622  '8' => __('Express easy'),
623  'P' => __('Express worldwide'),
624  'Q' => __('Medical express'),
625  'E' => __('Express 9:00'),
626  'F' => __('Freight worldwide'),
627  'H' => __('Economy select'),
628  'J' => __('Jumbo box'),
629  'M' => __('Express 10:30'),
630  'V' => __('Europack'),
631  'Y' => __('Express 12:00'),
632  ];
633 
634  if ($this->_isDomestic) {
635  return $docType + $nonDocType;
636  }
637  if ($doc == self::DHL_CONTENT_TYPE_DOC) {
638  // Documents shipping
639  return $docType;
640  } else {
641  // Services for shipping non-documents cargo
642  return $nonDocType;
643  }
644  }
645 
653  public function getDhlProductTitle($code)
654  {
655  $contentType = $this->getConfigData('content_type');
656  $dhlProducts = $this->getDhlProducts($contentType);
657 
658  return isset($dhlProducts[$code]) ? $dhlProducts[$code] : false;
659  }
660 
669  protected function _getWeight($weight, $maxWeight = false, $configWeightUnit = false)
670  {
671  if ($maxWeight) {
672  $configWeightUnit = \Zend_Measure_Weight::KILOGRAM;
673  } elseif ($configWeightUnit) {
674  $configWeightUnit = $this->getCode('dimensions_variables', $configWeightUnit);
675  } else {
676  $configWeightUnit = $this->getCode(
677  'dimensions_variables',
678  (string)$this->getConfigData('unit_of_measure')
679  );
680  }
681 
682  $countryWeightUnit = $this->getCode('dimensions_variables', $this->_getWeightUnit());
683 
684  if ($configWeightUnit != $countryWeightUnit) {
685  $weight = $this->_carrierHelper->convertMeasureWeight(
686  (float)$weight,
687  $configWeightUnit,
688  $countryWeightUnit
689  );
690  }
691 
692  return sprintf('%.3f', $weight);
693  }
694 
702  protected function _getAllItems()
703  {
704  $allItems = $this->_request->getAllItems();
705  $fullItems = [];
706 
707  foreach ($allItems as $item) {
708  if ($item->getProductType() == Type::TYPE_BUNDLE && $item->getProduct()->getShipmentType()) {
709  continue;
710  }
711 
712  $qty = $item->getQty();
713  $changeQty = true;
714  $checkWeight = true;
715  $decimalItems = [];
716 
717  if ($item->getParentItem()) {
718  if (!$item->getParentItem()->getProduct()->getShipmentType()) {
719  continue;
720  }
721  if ($item->getIsQtyDecimal()) {
722  $qty = $item->getParentItem()->getQty();
723  } else {
724  $qty = $item->getParentItem()->getQty() * $item->getQty();
725  }
726  }
727 
728  $itemWeight = $item->getWeight();
729  if ($item->getIsQtyDecimal() && $item->getProductType() != Type::TYPE_BUNDLE) {
730  $productId = $item->getProduct()->getId();
731  $stockItemDo = $this->stockRegistry->getStockItem($productId, $item->getStore()->getWebsiteId());
732  $isDecimalDivided = $stockItemDo->getIsDecimalDivided();
733  if ($isDecimalDivided) {
734  if ($stockItemDo->getEnableQtyIncrements()
735  && $stockItemDo->getQtyIncrements()
736  ) {
737  $itemWeight = $itemWeight * $stockItemDo->getQtyIncrements();
738  $qty = round($item->getWeight() / $itemWeight * $qty);
739  $changeQty = false;
740  } else {
741  $itemWeight = $this->_getWeight($itemWeight * $item->getQty());
742  $maxWeight = $this->_getWeight($this->_maxWeight, true);
743  if ($itemWeight > $maxWeight) {
744  $qtyItem = floor($itemWeight / $maxWeight);
745  $decimalItems[] = ['weight' => $maxWeight, 'qty' => $qtyItem];
746  $weightItem = $this->mathDivision->getExactDivision($itemWeight, $maxWeight);
747  if ($weightItem) {
748  $decimalItems[] = ['weight' => $weightItem, 'qty' => 1];
749  }
750  $checkWeight = false;
751  }
752  }
753  } else {
754  $itemWeight = $itemWeight * $item->getQty();
755  }
756  }
757 
758  if ($checkWeight && $this->_getWeight($itemWeight) > $this->_getWeight($this->_maxWeight, true)) {
759  return [];
760  }
761 
762  if ($changeQty
763  && !$item->getParentItem()
764  && $item->getIsQtyDecimal()
765  && $item->getProductType() != Type::TYPE_BUNDLE
766  ) {
767  $qty = 1;
768  }
769 
770  if (!empty($decimalItems)) {
771  foreach ($decimalItems as $decimalItem) {
772  $fullItems = array_merge(
773  $fullItems,
774  array_fill(0, $decimalItem['qty'] * $qty, $decimalItem['weight'])
775  );
776  }
777  } else {
778  $fullItems = array_merge($fullItems, array_fill(0, $qty, $this->_getWeight($itemWeight)));
779  }
780  }
781  sort($fullItems);
782 
783  return $fullItems;
784  }
785 
793  protected function _makePieces(\Magento\Shipping\Model\Simplexml\Element $nodeBkgDetails)
794  {
795  $divideOrderWeight = (string)$this->getConfigData('divide_order_weight');
796  $nodePieces = $nodeBkgDetails->addChild('Pieces', '', '');
797  $items = $this->_getAllItems();
798  $numberOfPieces = 0;
799 
800  if ($divideOrderWeight && !empty($items)) {
801  $maxWeight = $this->_getWeight($this->_maxWeight, true);
802  $sumWeight = 0;
803 
804  $reverseOrderItems = $items;
805  arsort($reverseOrderItems);
806 
807  foreach ($reverseOrderItems as $key => $weight) {
808  if (!isset($items[$key])) {
809  continue;
810  }
811  unset($items[$key]);
812  $sumWeight = $weight;
813  foreach ($items as $key => $weight) {
814  if ($sumWeight + $weight < $maxWeight) {
815  unset($items[$key]);
816  $sumWeight += $weight;
817  } elseif ($sumWeight + $weight > $maxWeight) {
818  $numberOfPieces++;
819  $nodePiece = $nodePieces->addChild('Piece', '', '');
820  $nodePiece->addChild('PieceID', $numberOfPieces);
821  $this->_addDimension($nodePiece);
822  $nodePiece->addChild('Weight', sprintf('%.3f', $sumWeight));
823  break;
824  } else {
825  unset($items[$key]);
826  $numberOfPieces++;
827  $sumWeight += $weight;
828  $nodePiece = $nodePieces->addChild('Piece', '', '');
829  $nodePiece->addChild('PieceID', $numberOfPieces);
830  $this->_addDimension($nodePiece);
831  $nodePiece->addChild('Weight', sprintf('%.3f', $sumWeight));
832  $sumWeight = 0;
833  break;
834  }
835  }
836  }
837  if ($sumWeight > 0) {
838  $numberOfPieces++;
839  $nodePiece = $nodePieces->addChild('Piece', '', '');
840  $nodePiece->addChild('PieceID', $numberOfPieces);
841  $this->_addDimension($nodePiece);
842  $nodePiece->addChild('Weight', sprintf('%.3f', $sumWeight));
843  }
844  } else {
845  $nodePiece = $nodePieces->addChild('Piece', '', '');
846  $nodePiece->addChild('PieceID', 1);
847  $this->_addDimension($nodePiece);
848  $nodePiece->addChild('Weight', $this->_getWeight($this->_rawRequest->getWeight()));
849  }
850 
851  $handlingAction = $this->getConfigData('handling_action');
852  if ($handlingAction == AbstractCarrier::HANDLING_ACTION_PERORDER || !$numberOfPieces) {
853  $numberOfPieces = 1;
854  }
855  $this->_numBoxes = $numberOfPieces;
856  }
857 
865  protected function _getDimension($dimension, $configWeightUnit = false)
866  {
867  if (!$configWeightUnit) {
868  $configWeightUnit = $this->getCode(
869  'dimensions_variables',
870  (string)$this->getConfigData('unit_of_measure')
871  );
872  } else {
873  $configWeightUnit = $this->getCode('dimensions_variables', $configWeightUnit);
874  }
875 
876  if ($configWeightUnit == \Zend_Measure_Weight::POUND) {
877  $configDimensionUnit = \Zend_Measure_Length::INCH;
878  } else {
879  $configDimensionUnit = \Zend_Measure_Length::CENTIMETER;
880  }
881 
882  $countryDimensionUnit = $this->getCode('dimensions_variables', $this->_getDimensionUnit());
883 
884  if ($configDimensionUnit != $countryDimensionUnit) {
885  $dimension = $this->_carrierHelper->convertMeasureDimension(
886  (float)$dimension,
887  $configDimensionUnit,
888  $countryDimensionUnit
889  );
890  }
891 
892  return sprintf('%.3f', $dimension);
893  }
894 
901  protected function _addDimension($nodePiece)
902  {
903  $sizeChecker = (string)$this->getConfigData('size');
904 
905  $height = $this->_getDimension((float)$this->getConfigData('height'));
906  $depth = $this->_getDimension((float)$this->getConfigData('depth'));
907  $width = $this->_getDimension((float)$this->getConfigData('width'));
908 
909  if ($sizeChecker && $height && $depth && $width) {
910  $nodePiece->addChild('Height', $height);
911  $nodePiece->addChild('Depth', $depth);
912  $nodePiece->addChild('Width', $width);
913  }
914  }
915 
921  protected function _getQuotes()
922  {
923  $responseBody = '';
924  try {
925  for ($offset = 0; $offset <= self::UNAVAILABLE_DATE_LOOK_FORWARD; $offset++) {
926  $debugPoint = [];
927 
928  $requestXml = $this->_buildQuotesRequestXml();
929  $date = date(self::REQUEST_DATE_FORMAT, strtotime($this->_getShipDate() . " +{$offset} days"));
930  $this->_setQuotesRequestXmlDate($requestXml, $date);
931 
932  $request = $requestXml->asXML();
933  $debugPoint['request'] = $this->filterDebugData($request);
934  $responseBody = $this->_getCachedQuotes($request);
935  $debugPoint['from_cache'] = $responseBody === null;
936 
937  if ($debugPoint['from_cache']) {
938  $responseBody = $this->_getQuotesFromServer($request);
939  }
940 
941  $debugPoint['response'] = $this->filterDebugData($responseBody);
942 
943  $bodyXml = $this->_xmlElFactory->create(['data' => $responseBody]);
944  $code = $bodyXml->xpath('//GetQuoteResponse/Note/Condition/ConditionCode');
945  if (isset($code[0]) && (int)$code[0] == self::CONDITION_CODE_SERVICE_DATE_UNAVAILABLE) {
946  $debugPoint['info'] = sprintf(__("DHL service is not available at %s date"), $date);
947  } else {
948  $this->_debug($debugPoint);
949  break;
950  }
951 
952  $this->_setCachedQuotes($request, $responseBody);
953  $this->_debug($debugPoint);
954  }
955  } catch (\Exception $e) {
956  $this->_errors[$e->getCode()] = $e->getMessage();
957  }
958 
959  return $this->_parseResponse($responseBody);
960  }
961 
968  protected function _getQuotesFromServer($request)
969  {
970  $client = $this->_httpClientFactory->create();
971  $client->setUri((string)$this->getConfigData('gateway_url'));
972  $client->setConfig(['maxredirects' => 0, 'timeout' => 30]);
973  $client->setRawData(utf8_encode($request));
974 
975  return $client->request(\Zend_Http_Client::POST)->getBody();
976  }
977 
983  protected function _buildQuotesRequestXml()
984  {
985  $rawRequest = $this->_rawRequest;
986  $xmlStr = '<?xml version = "1.0" encoding = "UTF-8"?>' .
987  '<p:DCTRequest xmlns:p="http://www.dhl.com" xmlns:p1="http://www.dhl.com/datatypes" ' .
988  'xmlns:p2="http://www.dhl.com/DCTRequestdatatypes" ' .
989  'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' .
990  'xsi:schemaLocation="http://www.dhl.com DCT-req.xsd "/>';
991  $xml = $this->_xmlElFactory->create(['data' => $xmlStr]);
992  $nodeGetQuote = $xml->addChild('GetQuote', '', '');
993  $nodeRequest = $nodeGetQuote->addChild('Request');
994 
995  $nodeServiceHeader = $nodeRequest->addChild('ServiceHeader');
996  $nodeServiceHeader->addChild('SiteID', (string)$this->getConfigData('id'));
997  $nodeServiceHeader->addChild('Password', (string)$this->getConfigData('password'));
998 
999  $nodeFrom = $nodeGetQuote->addChild('From');
1000  $nodeFrom->addChild('CountryCode', $rawRequest->getOrigCountryId());
1001  $nodeFrom->addChild('Postalcode', $rawRequest->getOrigPostal());
1002  $nodeFrom->addChild('City', $rawRequest->getOrigCity());
1003 
1004  $nodeBkgDetails = $nodeGetQuote->addChild('BkgDetails');
1005  $nodeBkgDetails->addChild('PaymentCountryCode', $rawRequest->getOrigCountryId());
1006  $nodeBkgDetails->addChild(
1007  'Date',
1008  (new \DateTime())->format(\Magento\Framework\Stdlib\DateTime::DATETIME_PHP_FORMAT)
1009  );
1010  $nodeBkgDetails->addChild('ReadyTime', 'PT' . (int)(string)$this->getConfigData('ready_time') . 'H00M');
1011 
1012  $nodeBkgDetails->addChild('DimensionUnit', $this->_getDimensionUnit());
1013  $nodeBkgDetails->addChild('WeightUnit', $this->_getWeightUnit());
1014 
1015  $this->_makePieces($nodeBkgDetails);
1016 
1017  $nodeBkgDetails->addChild('PaymentAccountNumber', (string)$this->getConfigData('account'));
1018 
1019  $nodeTo = $nodeGetQuote->addChild('To');
1020  $nodeTo->addChild('CountryCode', $rawRequest->getDestCountryId());
1021  $nodeTo->addChild('Postalcode', $rawRequest->getDestPostal());
1022  $nodeTo->addChild('City', $rawRequest->getDestCity());
1023 
1024  if ($this->isDutiable($rawRequest->getOrigCountryId(), $rawRequest->getDestCountryId())) {
1025  // IsDutiable flag and Dutiable node indicates that cargo is not a documentation
1026  $nodeBkgDetails->addChild('IsDutiable', 'Y');
1027  $nodeDutiable = $nodeGetQuote->addChild('Dutiable');
1028  $baseCurrencyCode = $this->_storeManager
1029  ->getWebsite($this->_request->getWebsiteId())
1030  ->getBaseCurrencyCode();
1031  $nodeDutiable->addChild('DeclaredCurrency', $baseCurrencyCode);
1032  $nodeDutiable->addChild('DeclaredValue', sprintf("%.2F", $rawRequest->getValue()));
1033  }
1034 
1035  return $xml;
1036  }
1037 
1045  protected function _setQuotesRequestXmlDate(\SimpleXMLElement $requestXml, $date)
1046  {
1047  $requestXml->GetQuote->BkgDetails->Date = $date;
1048 
1049  return $requestXml;
1050  }
1051 
1060  protected function _parseResponse($response)
1061  {
1062  $responseError = __('The response is in wrong format.');
1063  try {
1064  $this->xmlValidator->validate($response);
1065  $xml = simplexml_load_string($response);
1066  if (isset($xml->GetQuoteResponse->BkgDetails->QtdShp)) {
1067  foreach ($xml->GetQuoteResponse->BkgDetails->QtdShp as $quotedShipment) {
1068  $this->_addRate($quotedShipment);
1069  }
1070  } elseif (isset($xml->AirwayBillNumber)) {
1071  return $this->_prepareShippingLabelContent($xml);
1072  } else {
1073  $this->_errors[] = $responseError;
1074  }
1075  } catch (DocumentValidationException $e) {
1076  if ($e->getCode() > 0) {
1077  $this->_errors[$e->getCode()] = $e->getMessage();
1078  } else {
1079  $this->_errors[] = $e->getMessage();
1080  }
1081  }
1082  /* @var $result Result */
1083  $result = $this->_rateFactory->create();
1084  if ($this->_rates) {
1085  foreach ($this->_rates as $rate) {
1086  $method = $rate['service'];
1087  $data = $rate['data'];
1088  /* @var $rate \Magento\Quote\Model\Quote\Address\RateResult\Method */
1089  $rate = $this->_rateMethodFactory->create();
1090  $rate->setCarrier(self::CODE);
1091  $rate->setCarrierTitle($this->getConfigData('title'));
1092  $rate->setMethod($method);
1093  $rate->setMethodTitle($data['term']);
1094  $rate->setCost($data['price_total']);
1095  $rate->setPrice($data['price_total']);
1096  $result->append($rate);
1097  }
1098  } else {
1099  if (!empty($this->_errors)) {
1100  if ($this->_isShippingLabelFlag) {
1101  throw new \Magento\Framework\Exception\LocalizedException($responseError);
1102  }
1103  $this->debugErrors($this->_errors);
1104  }
1105  $result->append($this->getErrorMessage());
1106  }
1107 
1108  return $result;
1109  }
1110 
1118  protected function _addRate(\SimpleXMLElement $shipmentDetails)
1119  {
1120  if (isset($shipmentDetails->ProductShortName)
1121  && isset($shipmentDetails->ShippingCharge)
1122  && isset($shipmentDetails->GlobalProductCode)
1123  && isset($shipmentDetails->CurrencyCode)
1124  && array_key_exists((string)$shipmentDetails->GlobalProductCode, $this->getAllowedMethods())
1125  ) {
1126  // DHL product code, e.g. '3', 'A', 'Q', etc.
1127  $dhlProduct = (string)$shipmentDetails->GlobalProductCode;
1128  $totalEstimate = (float)(string)$shipmentDetails->ShippingCharge;
1129  $currencyCode = (string)$shipmentDetails->CurrencyCode;
1130  $baseCurrencyCode = $this->_storeManager->getWebsite($this->_request->getWebsiteId())
1131  ->getBaseCurrencyCode();
1132  $dhlProductDescription = $this->getDhlProductTitle($dhlProduct);
1133 
1134  if ($currencyCode != $baseCurrencyCode) {
1135  /* @var $currency \Magento\Directory\Model\Currency */
1136  $currency = $this->_currencyFactory->create();
1137  $rates = $currency->getCurrencyRates($currencyCode, [$baseCurrencyCode]);
1138  if (!empty($rates) && isset($rates[$baseCurrencyCode])) {
1139  // Convert to store display currency using store exchange rate
1140  $totalEstimate = $totalEstimate * $rates[$baseCurrencyCode];
1141  } else {
1142  $rates = $currency->getCurrencyRates($baseCurrencyCode, [$currencyCode]);
1143  if (!empty($rates) && isset($rates[$currencyCode])) {
1144  $totalEstimate = $totalEstimate / $rates[$currencyCode];
1145  }
1146  if (!isset($rates[$currencyCode]) || !$totalEstimate) {
1147  $totalEstimate = false;
1148  $this->_errors[] = __(
1149  'We had to skip DHL method %1 because we couldn\'t find exchange rate %2 (Base Currency).',
1150  $currencyCode,
1151  $baseCurrencyCode
1152  );
1153  }
1154  }
1155  }
1156  if ($totalEstimate) {
1157  $data = [
1158  'term' => $dhlProductDescription,
1159  'price_total' => $this->getMethodPrice($totalEstimate, $dhlProduct),
1160  ];
1161  if (!empty($this->_rates)) {
1162  foreach ($this->_rates as $product) {
1163  if ($product['data']['term'] == $data['term']
1164  && $product['data']['price_total'] == $data['price_total']
1165  ) {
1166  return $this;
1167  }
1168  }
1169  }
1170  $this->_rates[] = ['service' => $dhlProduct, 'data' => $data];
1171  } else {
1172  $this->_errors[] = __("Zero shipping charge for '%1'", $dhlProductDescription);
1173  }
1174  } else {
1175  $dhlProductDescription = false;
1176  if (isset($shipmentDetails->GlobalProductCode)) {
1177  $dhlProductDescription = $this->getDhlProductTitle((string)$shipmentDetails->GlobalProductCode);
1178  }
1179  $dhlProductDescription = $dhlProductDescription ? $dhlProductDescription : __("DHL");
1180  $this->_errors[] = __("Zero shipping charge for '%1'", $dhlProductDescription);
1181  }
1182 
1183  return $this;
1184  }
1185 
1192  protected function _getDimensionUnit()
1193  {
1194  $countryId = $this->_rawRequest->getOrigCountryId();
1195  $measureUnit = $this->getCountryParams($countryId)->getMeasureUnit();
1196  if (empty($measureUnit)) {
1197  throw new \Magento\Framework\Exception\LocalizedException(
1198  __("Cannot identify measure unit for %1", $countryId)
1199  );
1200  }
1201 
1202  return $measureUnit;
1203  }
1204 
1211  protected function _getWeightUnit()
1212  {
1213  $countryId = $this->_rawRequest->getOrigCountryId();
1214  $weightUnit = $this->getCountryParams($countryId)->getWeightUnit();
1215  if (empty($weightUnit)) {
1216  throw new \Magento\Framework\Exception\LocalizedException(
1217  __("Cannot identify weight unit for %1", $countryId)
1218  );
1219  }
1220 
1221  return $weightUnit;
1222  }
1223 
1232  protected function getCountryParams($countryCode)
1233  {
1234  if (empty($this->_countryParams)) {
1235  $etcPath = $this->_configReader->getModuleDir(Dir::MODULE_ETC_DIR, 'Magento_Dhl');
1236  $directoryRead = $this->readFactory->create($etcPath);
1237  $countriesXml = $directoryRead->readFile('countries.xml');
1238  $this->_countryParams = $this->_xmlElFactory->create(['data' => $countriesXml]);
1239  }
1240  if (isset($this->_countryParams->{$countryCode})) {
1241  $countryParams = new \Magento\Framework\DataObject($this->_countryParams->{$countryCode}->asArray());
1242  }
1243  return isset($countryParams) ? $countryParams : new \Magento\Framework\DataObject();
1244  }
1245 
1252  protected function _doShipmentRequest(\Magento\Framework\DataObject $request)
1253  {
1254  $this->_prepareShipmentRequest($request);
1255  $this->_mapRequestToShipment($request);
1256  $this->setRequest($request);
1257 
1258  return $this->_doRequest();
1259  }
1260 
1268  public function proccessAdditionalValidation(\Magento\Framework\DataObject $request)
1269  {
1270  return $this->processAdditionalValidation($request);
1271  }
1272 
1279  public function processAdditionalValidation(\Magento\Framework\DataObject $request)
1280  {
1281  //Skip by item validation if there is no items in request
1282  if (!count($this->getAllItems($request))) {
1283  $this->_errors[] = __('There is no items in this order');
1284  }
1285 
1286  $countryParams = $this->getCountryParams(
1287  $this->_scopeConfig->getValue(
1288  Shipment::XML_PATH_STORE_COUNTRY_ID,
1289  \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
1290  $request->getStoreId()
1291  )
1292  );
1293  if (!$countryParams->getData()) {
1294  $this->_errors[] = __('Please specify origin country.');
1295  }
1296 
1297  if (!empty($this->_errors)) {
1298  $this->debugErrors($this->_errors);
1299 
1300  return false;
1301  }
1302 
1303  return $this;
1304  }
1305 
1313  public function getContainerTypes(\Magento\Framework\DataObject $params = null)
1314  {
1315  return [
1316  self::DHL_CONTENT_TYPE_DOC => __('Documents'),
1317  self::DHL_CONTENT_TYPE_NON_DOC => __('Non Documents')
1318  ];
1319  }
1320 
1328  protected function _mapRequestToShipment(\Magento\Framework\DataObject $request)
1329  {
1330  $request->setOrigCountryId($request->getShipperAddressCountryCode());
1331  $this->setRawRequest($request);
1332  $customsValue = 0;
1333  $packageWeight = 0;
1334  $packages = $request->getPackages();
1335  foreach ($packages as &$piece) {
1336  $params = $piece['params'];
1337  if ($params['width'] || $params['length'] || $params['height']) {
1338  $minValue = $this->_getMinDimension($params['dimension_units']);
1339  if ($params['width'] < $minValue || $params['length'] < $minValue || $params['height'] < $minValue) {
1340  $message = __('Height, width and length should be equal or greater than %1', $minValue);
1341  throw new \Magento\Framework\Exception\LocalizedException($message);
1342  }
1343  }
1344 
1345  $weightUnits = $piece['params']['weight_units'];
1346  $piece['params']['height'] = $this->_getDimension($piece['params']['height'], $weightUnits);
1347  $piece['params']['length'] = $this->_getDimension($piece['params']['length'], $weightUnits);
1348  $piece['params']['width'] = $this->_getDimension($piece['params']['width'], $weightUnits);
1349  $piece['params']['dimension_units'] = $this->_getDimensionUnit();
1350  $piece['params']['weight'] = $this->_getWeight($piece['params']['weight'], false, $weightUnits);
1351  $piece['params']['weight_units'] = $this->_getWeightUnit();
1352 
1353  $customsValue += $piece['params']['customs_value'];
1354  $packageWeight += $piece['params']['weight'];
1355  }
1356 
1357  $request->setPackages($packages)
1358  ->setPackageWeight($packageWeight)
1359  ->setPackageValue($customsValue)
1360  ->setValueWithDiscount($customsValue)
1361  ->setPackageCustomsValue($customsValue)
1362  ->setFreeMethodWeight(0);
1363  }
1364 
1371  protected function _getMinDimension($dimensionUnit)
1372  {
1373  return $dimensionUnit == "CENTIMETER" ? self::DIMENSION_MIN_CM : self::DIMENSION_MIN_IN;
1374  }
1375 
1385  protected function _doRequest()
1386  {
1387  $rawRequest = $this->_request;
1388 
1389  $originRegion = $this->getCountryParams(
1390  $this->_scopeConfig->getValue(
1391  Shipment::XML_PATH_STORE_COUNTRY_ID,
1392  \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
1393  $this->getStore()
1394  )
1395  )->getRegion();
1396 
1397  if (!$originRegion) {
1398  throw new \Magento\Framework\Exception\LocalizedException(__('Wrong Region'));
1399  }
1400 
1401  if ($originRegion == 'AM') {
1402  $originRegion = '';
1403  }
1404 
1405  $xmlStr = '<?xml version="1.0" encoding="UTF-8"?>' .
1406  '<req:ShipmentValidateRequest' .
1407  $originRegion .
1408  ' xmlns:req="http://www.dhl.com"' .
1409  ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' .
1410  ' xsi:schemaLocation="http://www.dhl.com ship-val-req' .
1411  ($originRegion ? '_' .
1412  $originRegion : '') .
1413  '.xsd" />';
1414  $xml = $this->_xmlElFactory->create(['data' => $xmlStr]);
1415 
1416  $nodeRequest = $xml->addChild('Request', '', '');
1417  $nodeServiceHeader = $nodeRequest->addChild('ServiceHeader');
1418  $nodeServiceHeader->addChild('SiteID', (string)$this->getConfigData('id'));
1419  $nodeServiceHeader->addChild('Password', (string)$this->getConfigData('password'));
1420 
1421  if (!$originRegion) {
1422  $xml->addChild('RequestedPickupTime', 'N', '');
1423  }
1424  if ($originRegion !== 'AP') {
1425  $xml->addChild('NewShipper', 'N', '');
1426  }
1427  $xml->addChild('LanguageCode', 'EN', '');
1428  $xml->addChild('PiecesEnabled', 'Y', '');
1429 
1431  $nodeBilling = $xml->addChild('Billing', '', '');
1432  $nodeBilling->addChild('ShipperAccountNumber', (string)$this->getConfigData('account'));
1439  $nodeBilling->addChild('ShippingPaymentType', 'S');
1440 
1444  $nodeBilling->addChild('BillingAccountNumber', (string)$this->getConfigData('account'));
1445  $nodeBilling->addChild('DutyPaymentType', 'S');
1446  $nodeBilling->addChild('DutyAccountNumber', (string)$this->getConfigData('account'));
1447 
1449  $nodeConsignee = $xml->addChild('Consignee', '', '');
1450 
1451  $companyName = $rawRequest->getRecipientContactCompanyName() ? $rawRequest
1452  ->getRecipientContactCompanyName() : $rawRequest
1453  ->getRecipientContactPersonName();
1454 
1455  $nodeConsignee->addChild('CompanyName', substr($companyName, 0, 35));
1456 
1457  $address = $rawRequest->getRecipientAddressStreet1() . ' ' . $rawRequest->getRecipientAddressStreet2();
1458  $address = $this->string->split($address, 35, false, true);
1459  if (is_array($address)) {
1460  foreach ($address as $addressLine) {
1461  $nodeConsignee->addChild('AddressLine', $addressLine);
1462  }
1463  } else {
1464  $nodeConsignee->addChild('AddressLine', $address);
1465  }
1466 
1467  $nodeConsignee->addChild('City', $rawRequest->getRecipientAddressCity());
1468  if ($originRegion !== 'AP') {
1469  $nodeConsignee->addChild('Division', $rawRequest->getRecipientAddressStateOrProvinceCode());
1470  }
1471  $nodeConsignee->addChild('PostalCode', $rawRequest->getRecipientAddressPostalCode());
1472  $nodeConsignee->addChild('CountryCode', $rawRequest->getRecipientAddressCountryCode());
1473  $nodeConsignee->addChild(
1474  'CountryName',
1475  $this->getCountryParams($rawRequest->getRecipientAddressCountryCode())->getName()
1476  );
1477  $nodeContact = $nodeConsignee->addChild('Contact');
1478  $nodeContact->addChild('PersonName', substr($rawRequest->getRecipientContactPersonName(), 0, 34));
1479  $nodeContact->addChild('PhoneNumber', substr($rawRequest->getRecipientContactPhoneNumber(), 0, 24));
1480 
1486  $nodeCommodity = $xml->addChild('Commodity', '', '');
1487  $nodeCommodity->addChild('CommodityCode', '1');
1488 
1490  if ($this->isDutiable(
1491  $rawRequest->getShipperAddressCountryCode(),
1492  $rawRequest->getRecipientAddressCountryCode()
1493  )) {
1494  $nodeDutiable = $xml->addChild('Dutiable', '', '');
1495  $nodeDutiable->addChild(
1496  'DeclaredValue',
1497  sprintf("%.2F", $rawRequest->getOrderShipment()->getOrder()->getSubtotal())
1498  );
1499  $baseCurrencyCode = $this->_storeManager->getWebsite($rawRequest->getWebsiteId())->getBaseCurrencyCode();
1500  $nodeDutiable->addChild('DeclaredCurrency', $baseCurrencyCode);
1501  }
1502 
1508  $nodeReference = $xml->addChild('Reference', '', '');
1509  $nodeReference->addChild('ReferenceID', 'shipment reference');
1510  $nodeReference->addChild('ReferenceType', 'St');
1511 
1513  $this->_shipmentDetails($xml, $rawRequest, $originRegion);
1514 
1516  $nodeShipper = $xml->addChild('Shipper', '', '');
1517  $nodeShipper->addChild('ShipperID', (string)$this->getConfigData('account'));
1518  $nodeShipper->addChild('CompanyName', $rawRequest->getShipperContactCompanyName());
1519  if ($originRegion !== 'AP') {
1520  $nodeShipper->addChild('RegisteredAccount', (string)$this->getConfigData('account'));
1521  }
1522 
1523  $address = $rawRequest->getShipperAddressStreet1() . ' ' . $rawRequest->getShipperAddressStreet2();
1524  $address = $this->string->split($address, 35, false, true);
1525  if (is_array($address)) {
1526  foreach ($address as $addressLine) {
1527  $nodeShipper->addChild('AddressLine', $addressLine);
1528  }
1529  } else {
1530  $nodeShipper->addChild('AddressLine', $address);
1531  }
1532 
1533  $nodeShipper->addChild('City', $rawRequest->getShipperAddressCity());
1534  if ($originRegion !== 'AP') {
1535  $nodeShipper->addChild('Division', $rawRequest->getShipperAddressStateOrProvinceCode());
1536  }
1537  $nodeShipper->addChild('PostalCode', $rawRequest->getShipperAddressPostalCode());
1538  $nodeShipper->addChild('CountryCode', $rawRequest->getShipperAddressCountryCode());
1539  $nodeShipper->addChild(
1540  'CountryName',
1541  $this->getCountryParams($rawRequest->getShipperAddressCountryCode())->getName()
1542  );
1543  $nodeContact = $nodeShipper->addChild('Contact', '', '');
1544  $nodeContact->addChild('PersonName', substr($rawRequest->getShipperContactPersonName(), 0, 34));
1545  $nodeContact->addChild('PhoneNumber', substr($rawRequest->getShipperContactPhoneNumber(), 0, 24));
1546 
1547  $xml->addChild('LabelImageFormat', 'PDF', '');
1548 
1549  $request = $xml->asXML();
1550  if (!$request && !(mb_detect_encoding($request) == 'UTF-8')) {
1551  $request = utf8_encode($request);
1552  }
1553 
1554  $responseBody = $this->_getCachedQuotes($request);
1555  if ($responseBody === null) {
1556  $debugData = ['request' => $this->filterDebugData($request)];
1557  try {
1559  $client = $this->_httpClientFactory->create();
1560  $client->setUri((string)$this->getConfigData('gateway_url'));
1561  $client->setConfig(['maxredirects' => 0, 'timeout' => 30]);
1562  $client->setRawData($request);
1563  $responseBody = $client->request(\Magento\Framework\HTTP\ZendClient::POST)->getBody();
1564  $responseBody = utf8_decode($responseBody);
1565  $debugData['result'] = $this->filterDebugData($responseBody);
1566  $this->_setCachedQuotes($request, $responseBody);
1567  } catch (\Exception $e) {
1568  $this->_errors[$e->getCode()] = $e->getMessage();
1569  $responseBody = '';
1570  }
1571  $this->_debug($debugData);
1572  }
1573  $this->_isShippingLabelFlag = true;
1574 
1575  return $this->_parseResponse($responseBody);
1576  }
1577 
1588  protected function _shipmentDetails($xml, $rawRequest, $originRegion = '')
1589  {
1590  $nodeShipmentDetails = $xml->addChild('ShipmentDetails', '', '');
1591  $nodeShipmentDetails->addChild('NumberOfPieces', count($rawRequest->getPackages()));
1592 
1593  if ($originRegion) {
1594  $nodeShipmentDetails->addChild(
1595  'CurrencyCode',
1596  $this->_storeManager->getWebsite($this->_request->getWebsiteId())->getBaseCurrencyCode()
1597  );
1598  }
1599 
1600  $nodePieces = $nodeShipmentDetails->addChild('Pieces', '', '');
1601 
1602  /*
1603  * Package type
1604  * EE (DHL Express Envelope), OD (Other DHL Packaging), CP (Custom Packaging)
1605  * DC (Document), DM (Domestic), ED (Express Document), FR (Freight)
1606  * BD (Jumbo Document), BP (Jumbo Parcel), JD (Jumbo Junior Document)
1607  * JP (Jumbo Junior Parcel), PA (Parcel), DF (DHL Flyer)
1608  */
1609  $i = 0;
1610  foreach ($rawRequest->getPackages() as $package) {
1611  $nodePiece = $nodePieces->addChild('Piece', '', '');
1612  $packageType = 'EE';
1613  if ($package['params']['container'] == self::DHL_CONTENT_TYPE_NON_DOC) {
1614  $packageType = 'CP';
1615  }
1616  $nodePiece->addChild('PieceID', ++$i);
1617  $nodePiece->addChild('PackageType', $packageType);
1618  $nodePiece->addChild('Weight', sprintf('%.1f', $package['params']['weight']));
1619  $params = $package['params'];
1620  if ($params['width'] && $params['length'] && $params['height']) {
1621  if (!$originRegion) {
1622  $nodePiece->addChild('Width', round($params['width']));
1623  $nodePiece->addChild('Height', round($params['height']));
1624  $nodePiece->addChild('Depth', round($params['length']));
1625  } else {
1626  $nodePiece->addChild('Depth', round($params['length']));
1627  $nodePiece->addChild('Width', round($params['width']));
1628  $nodePiece->addChild('Height', round($params['height']));
1629  }
1630  }
1631  $content = [];
1632  foreach ($package['items'] as $item) {
1633  $content[] = $item['name'];
1634  }
1635  $nodePiece->addChild('PieceContents', substr(implode(',', $content), 0, 34));
1636  }
1637 
1638  if (!$originRegion) {
1639  $nodeShipmentDetails->addChild('Weight', sprintf('%.1f', $rawRequest->getPackageWeight()));
1640  $nodeShipmentDetails->addChild('WeightUnit', substr($this->_getWeightUnit(), 0, 1));
1641  $nodeShipmentDetails->addChild('GlobalProductCode', $rawRequest->getShippingMethod());
1642  $nodeShipmentDetails->addChild('LocalProductCode', $rawRequest->getShippingMethod());
1643  $nodeShipmentDetails->addChild('Date', $this->_coreDate->date('Y-m-d'));
1644  $nodeShipmentDetails->addChild('Contents', 'DHL Parcel');
1650  $nodeShipmentDetails->addChild('DoorTo', 'DD');
1651  $nodeShipmentDetails->addChild('DimensionUnit', substr($this->_getDimensionUnit(), 0, 1));
1652  if ($package['params']['container'] == self::DHL_CONTENT_TYPE_NON_DOC) {
1653  $packageType = 'CP';
1654  }
1655  $nodeShipmentDetails->addChild('PackageType', $packageType);
1656  if ($this->isDutiable($rawRequest->getOrigCountryId(), $rawRequest->getDestCountryId())) {
1657  $nodeShipmentDetails->addChild('IsDutiable', 'Y');
1658  }
1659  $nodeShipmentDetails->addChild(
1660  'CurrencyCode',
1661  $this->_storeManager->getWebsite($this->_request->getWebsiteId())->getBaseCurrencyCode()
1662  );
1663  } else {
1664  if ($package['params']['container'] == self::DHL_CONTENT_TYPE_NON_DOC) {
1665  $packageType = 'CP';
1666  }
1667  $nodeShipmentDetails->addChild('PackageType', $packageType);
1668  $nodeShipmentDetails->addChild('Weight', sprintf('%.3f', $rawRequest->getPackageWeight()));
1669  $nodeShipmentDetails->addChild('DimensionUnit', substr($this->_getDimensionUnit(), 0, 1));
1670  $nodeShipmentDetails->addChild('WeightUnit', substr($this->_getWeightUnit(), 0, 1));
1671  $nodeShipmentDetails->addChild('GlobalProductCode', $rawRequest->getShippingMethod());
1672  $nodeShipmentDetails->addChild('LocalProductCode', $rawRequest->getShippingMethod());
1673 
1679  $nodeShipmentDetails->addChild('DoorTo', 'DD');
1680  $nodeShipmentDetails->addChild('Date', $this->_coreDate->date('Y-m-d'));
1681  $nodeShipmentDetails->addChild('Contents', 'DHL Parcel TEST');
1682  }
1683  }
1684 
1691  public function getTracking($trackings)
1692  {
1693  if (!is_array($trackings)) {
1694  $trackings = [$trackings];
1695  }
1696  $this->_getXMLTracking($trackings);
1697 
1698  return $this->_result;
1699  }
1700 
1707  protected function _getXMLTracking($trackings)
1708  {
1709  $xmlStr = '<?xml version="1.0" encoding="UTF-8"?>' .
1710  '<req:KnownTrackingRequest' .
1711  ' xmlns:req="http://www.dhl.com"' .
1712  ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' .
1713  ' xsi:schemaLocation="http://www.dhl.com TrackingRequestKnown.xsd" />';
1714 
1715  $xml = $this->_xmlElFactory->create(['data' => $xmlStr]);
1716 
1717  $requestNode = $xml->addChild('Request', '', '');
1718  $serviceHeaderNode = $requestNode->addChild('ServiceHeader', '', '');
1719  $serviceHeaderNode->addChild('SiteID', (string)$this->getConfigData('id'));
1720  $serviceHeaderNode->addChild('Password', (string)$this->getConfigData('password'));
1721 
1722  $xml->addChild('LanguageCode', 'EN', '');
1723  foreach ($trackings as $tracking) {
1724  $xml->addChild('AWBNumber', $tracking, '');
1725  }
1731  $xml->addChild('LevelOfDetails', 'ALL_CHECK_POINTS', '');
1732 
1743  //$xml->addChild('PiecesEnabled', 'ALL_CHECK_POINTS');
1744 
1745  $request = $xml->asXML();
1746  $request = utf8_encode($request);
1747 
1748  $responseBody = $this->_getCachedQuotes($request);
1749  if ($responseBody === null) {
1750  $debugData = ['request' => $this->filterDebugData($request)];
1751  try {
1753  $client = $this->_httpClientFactory->create();
1754  $client->setUri((string)$this->getConfigData('gateway_url'));
1755  $client->setConfig(['maxredirects' => 0, 'timeout' => 30]);
1756  $client->setRawData($request);
1757  $responseBody = $client->request(\Magento\Framework\HTTP\ZendClient::POST)->getBody();
1758  $debugData['result'] = $this->filterDebugData($responseBody);
1759  $this->_setCachedQuotes($request, $responseBody);
1760  } catch (\Exception $e) {
1761  $this->_errors[$e->getCode()] = $e->getMessage();
1762  $responseBody = '';
1763  }
1764  $this->_debug($debugData);
1765  }
1766 
1767  $this->_parseXmlTrackingResponse($trackings, $responseBody);
1768  }
1769 
1779  protected function _parseXmlTrackingResponse($trackings, $response)
1780  {
1781  $errorTitle = __('Unable to retrieve tracking');
1782  $resultArr = [];
1783 
1784  if (strlen(trim($response)) > 0) {
1785  $xml = $this->parseXml($response, \Magento\Shipping\Model\Simplexml\Element::class);
1786  if (!is_object($xml)) {
1787  $errorTitle = __('Response is in the wrong format');
1788  }
1789  if (is_object($xml)
1790  && (isset($xml->Response->Status->ActionStatus)
1791  && $xml->Response->Status->ActionStatus == 'Failure'
1792  || isset($xml->GetQuoteResponse->Note->Condition))
1793  ) {
1794  if (isset($xml->Response->Status->Condition)) {
1795  $nodeCondition = $xml->Response->Status->Condition;
1796  }
1797  $code = isset($nodeCondition->ConditionCode) ? (string)$nodeCondition->ConditionCode : 0;
1798  $data = isset($nodeCondition->ConditionData) ? (string)$nodeCondition->ConditionData : '';
1799  $this->_errors[$code] = __('Error #%1 : %2', $code, $data);
1800  } elseif (is_object($xml) && is_object($xml->AWBInfo)) {
1801  foreach ($xml->AWBInfo as $awbinfo) {
1802  $awbinfoData = [];
1803  $trackNum = isset($awbinfo->AWBNumber) ? (string)$awbinfo->AWBNumber : '';
1804  if (!is_object($awbinfo) || !$awbinfo->ShipmentInfo) {
1805  $this->_errors[$trackNum] = __('Unable to retrieve tracking');
1806  continue;
1807  }
1808  $shipmentInfo = $awbinfo->ShipmentInfo;
1809 
1810  if ($shipmentInfo->ShipmentDesc) {
1811  $awbinfoData['service'] = (string)$shipmentInfo->ShipmentDesc;
1812  }
1813 
1814  $awbinfoData['weight'] = (string)$shipmentInfo->Weight . ' ' . (string)$shipmentInfo->WeightUnit;
1815 
1816  $packageProgress = [];
1817  if (isset($shipmentInfo->ShipmentEvent)) {
1818  foreach ($shipmentInfo->ShipmentEvent as $shipmentEvent) {
1819  $shipmentEventArray = [];
1820  $shipmentEventArray['activity'] = (string)$shipmentEvent->ServiceEvent->EventCode
1821  . ' ' . (string)$shipmentEvent->ServiceEvent->Description;
1822  $shipmentEventArray['deliverydate'] = (string)$shipmentEvent->Date;
1823  $shipmentEventArray['deliverytime'] = (string)$shipmentEvent->Time;
1824  $shipmentEventArray['deliverylocation'] = (string)$shipmentEvent->ServiceArea
1825  ->Description . ' [' . (string)$shipmentEvent->ServiceArea->ServiceAreaCode . ']';
1826  $packageProgress[] = $shipmentEventArray;
1827  }
1828  $awbinfoData['progressdetail'] = $packageProgress;
1829  }
1830  $resultArr[$trackNum] = $awbinfoData;
1831  }
1832  }
1833  }
1834 
1835  $result = $this->_trackFactory->create();
1836 
1837  if (!empty($resultArr)) {
1838  foreach ($resultArr as $trackNum => $data) {
1839  $tracking = $this->_trackStatusFactory->create();
1840  $tracking->setCarrier($this->_code);
1841  $tracking->setCarrierTitle($this->getConfigData('title'));
1842  $tracking->setTracking($trackNum);
1843  $tracking->addData($data);
1844  $result->append($tracking);
1845  }
1846  }
1847 
1848  if (!empty($this->_errors) || empty($resultArr)) {
1849  $resultArr = !empty($this->_errors) ? $this->_errors : $trackings;
1850  foreach ($resultArr as $trackNum => $err) {
1851  $error = $this->_trackErrorFactory->create();
1852  $error->setCarrier($this->_code);
1853  $error->setCarrierTitle($this->getConfigData('title'));
1854  $error->setTracking(!empty($this->_errors) ? $trackNum : $err);
1855  $error->setErrorMessage(!empty($this->_errors) ? $err : $errorTitle);
1856  $result->append($error);
1857  }
1858  }
1859 
1860  $this->_result = $result;
1861  }
1862 
1871  protected function _getPerpackagePrice($cost, $handlingType, $handlingFee)
1872  {
1873  if ($handlingType == AbstractCarrier::HANDLING_TYPE_PERCENT) {
1874  return $cost + $cost * $this->_numBoxes * $handlingFee / 100;
1875  }
1876 
1877  return $cost + $this->_numBoxes * $handlingFee;
1878  }
1879 
1887  public function requestToShipment($request)
1888  {
1889  $packages = $request->getPackages();
1890  if (!is_array($packages) || !$packages) {
1891  throw new \Magento\Framework\Exception\LocalizedException(__('No packages for request'));
1892  }
1893  $result = $this->_doShipmentRequest($request);
1894 
1895  $response = new \Magento\Framework\DataObject(
1896  [
1897  'info' => [
1898  [
1899  'tracking_number' => $result->getTrackingNumber(),
1900  'label_content' => $result->getShippingLabelContent(),
1901  ],
1902  ],
1903  ]
1904  );
1905 
1906  $request->setMasterTrackingId($result->getTrackingNumber());
1907 
1908  return $response;
1909  }
1910 
1918  protected function _checkDomesticStatus($origCountryCode, $destCountryCode)
1919  {
1920  $this->_isDomestic = false;
1921 
1922  $origCountry = (string)$this->getCountryParams($origCountryCode)->getData('name');
1923  $destCountry = (string)$this->getCountryParams($destCountryCode)->getData('name');
1924  $isDomestic = (string)$this->getCountryParams($destCountryCode)->getData('domestic');
1925 
1926  if (($origCountry == $destCountry && $isDomestic)
1927  || ($this->_carrierHelper->isCountryInEU($origCountryCode)
1928  && $this->_carrierHelper->isCountryInEU($destCountryCode)
1929  )
1930  ) {
1931  $this->_isDomestic = true;
1932  }
1933 
1934  return $this->_isDomestic;
1935  }
1936 
1944  protected function _prepareShippingLabelContent(\SimpleXMLElement $xml)
1945  {
1946  $result = new \Magento\Framework\DataObject();
1947  try {
1948  if (!isset($xml->AirwayBillNumber) || !isset($xml->LabelImage->OutputImage)) {
1949  throw new \Magento\Framework\Exception\LocalizedException(__('Unable to retrieve shipping label'));
1950  }
1951  $result->setTrackingNumber((string)$xml->AirwayBillNumber);
1952  $labelContent = (string)$xml->LabelImage->OutputImage;
1953  $result->setShippingLabelContent(base64_decode($labelContent));
1954  } catch (\Exception $e) {
1955  throw new \Magento\Framework\Exception\LocalizedException(__($e->getMessage()));
1956  }
1957 
1958  return $result;
1959  }
1960 
1967  protected function isDutiable($origCountryId, $destCountryId)
1968  {
1969  $this->_checkDomesticStatus($origCountryId, $destCountryId);
1970 
1971  return
1972  self::DHL_CONTENT_TYPE_NON_DOC == $this->getConfigData('content_type')
1973  || !$this->_isDomestic;
1974  }
1975 }
proccessAdditionalValidation(\Magento\Framework\DataObject $request)
Definition: Carrier.php:1268
_addRate(\SimpleXMLElement $shipmentDetails)
Definition: Carrier.php:1118
processAdditionalValidation(\Magento\Framework\DataObject $request)
Definition: Carrier.php:1279
$response
Definition: 404.php:11
if(!defined( 'PHP_VERSION_ID')||!(PHP_VERSION_ID===70002||PHP_VERSION_ID===70004||PHP_VERSION_ID >=70006))
Definition: bootstrap.php:14
elseif(isset( $params[ 'redirect_parent']))
Definition: iframe.phtml:17
getCode($type, $code='')
Definition: Carrier.php:549
getContainerTypes(\Magento\Framework\DataObject $params=null)
Definition: Carrier.php:1313
_checkDomesticStatus($origCountryCode, $destCountryCode)
Definition: Carrier.php:1918
_getPerpackagePrice($cost, $handlingType, $handlingFee)
Definition: Carrier.php:1871
_doShipmentRequest(\Magento\Framework\DataObject $request)
Definition: Carrier.php:1252
$storeManager
__()
Definition: __.php:13
$message
$rates
Definition: tax.phtml:35
setRequest(\Magento\Framework\DataObject $request)
Definition: Carrier.php:409
_setFreeMethodRequest($freeMethod)
Definition: Carrier.php:363
$logger
isDutiable($origCountryId, $destCountryId)
Definition: Carrier.php:1967
$address
Definition: customer.php:38
__construct(\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory, \Psr\Log\LoggerInterface $logger, Security $xmlSecurity, \Magento\Shipping\Model\Simplexml\ElementFactory $xmlElFactory, \Magento\Shipping\Model\Rate\ResultFactory $rateFactory, \Magento\Quote\Model\Quote\Address\RateResult\MethodFactory $rateMethodFactory, \Magento\Shipping\Model\Tracking\ResultFactory $trackFactory, \Magento\Shipping\Model\Tracking\Result\ErrorFactory $trackErrorFactory, \Magento\Shipping\Model\Tracking\Result\StatusFactory $trackStatusFactory, \Magento\Directory\Model\RegionFactory $regionFactory, \Magento\Directory\Model\CountryFactory $countryFactory, \Magento\Directory\Model\CurrencyFactory $currencyFactory, \Magento\Directory\Helper\Data $directoryData, \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry, \Magento\Shipping\Helper\Carrier $carrierHelper, \Magento\Framework\Stdlib\DateTime\DateTime $coreDate, \Magento\Framework\Module\Dir\Reader $configReader, \Magento\Store\Model\StoreManagerInterface $storeManager, \Magento\Framework\Stdlib\StringUtils $string, \Magento\Framework\Math\Division $mathDivision, \Magento\Framework\Filesystem\Directory\ReadFactory $readFactory, \Magento\Framework\Stdlib\DateTime $dateTime, \Magento\Framework\HTTP\ZendClientFactory $httpClientFactory, array $data=[], \Magento\Dhl\Model\Validator\XmlValidator $xmlValidator=null)
Definition: Carrier.php:238
$type
Definition: item.phtml:13
$methods
Definition: billing.phtml:71
$value
Definition: gender.phtml:16
_addParams(\Magento\Framework\DataObject $requestObject)
Definition: Carrier.php:387
const XML_PATH_EU_COUNTRIES_LIST
Definition: Carrier.php:45
_getDimension($dimension, $configWeightUnit=false)
Definition: Carrier.php:865
_shipmentDetails($xml, $rawRequest, $originRegion='')
Definition: Carrier.php:1588
_addDimension($nodePiece)
Definition: Carrier.php:901
$method
Definition: info.phtml:13
_getQuotesFromServer($request)
Definition: Carrier.php:968
_setQuotesRequestXmlDate(\SimpleXMLElement $requestXml, $date)
Definition: Carrier.php:1045
_prepareShippingLabelContent(\SimpleXMLElement $xml)
Definition: Carrier.php:1944
_makePieces(\Magento\Shipping\Model\Simplexml\Element $nodeBkgDetails)
Definition: Carrier.php:793
_getDefaultValue($origValue, $pathToValue)
Definition: Carrier.php:307
$params[\Magento\Store\Model\StoreManager::PARAM_RUN_CODE]
Definition: website.php:18
_mapRequestToShipment(\Magento\Framework\DataObject $request)
Definition: Carrier.php:1328
_getMinDimension($dimensionUnit)
Definition: Carrier.php:1371
$i
Definition: gallery.phtml:31
getCountryParams($countryCode)
Definition: Carrier.php:1232
collectRates(RateRequest $request)
Definition: Carrier.php:326
$code
Definition: info.phtml:12
_parseXmlTrackingResponse($trackings, $response)
Definition: Carrier.php:1779
_getWeight($weight, $maxWeight=false, $configWeightUnit=false)
Definition: Carrier.php:669
$dateTime
$items