Carrier.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. <?php
  2. /**
  3. * Refer to LICENSE.txt distributed with the Temando Shipping module for notice of license
  4. */
  5. namespace Temando\Shipping\Model\Shipping;
  6. use Magento\Framework\App\Config\ScopeConfigInterface;
  7. use Magento\Framework\Exception\LocalizedException;
  8. use Magento\Framework\Exception\NoSuchEntityException;
  9. use Magento\Framework\Message\ManagerInterface;
  10. use Magento\Quote\Model\Quote\Address\RateRequest;
  11. use Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory;
  12. use Magento\Quote\Model\Quote\Address\RateResult\MethodFactory;
  13. use Magento\Sales\Api\Data\ShipmentTrackInterface;
  14. use Magento\Shipping\Model\Carrier\AbstractCarrier;
  15. use Magento\Shipping\Model\Carrier\CarrierInterface;
  16. use Magento\Shipping\Model\Rate\ResultFactory;
  17. use Magento\Shipping\Model\Tracking\Result\StatusFactory;
  18. use Psr\Log\LoggerInterface;
  19. use Psr\Log\LogLevel;
  20. use Temando\Shipping\Model\Checkout\RateRequest\QuotingDataInitializer;
  21. use Temando\Shipping\Model\ExperienceInterface;
  22. use Temando\Shipping\Model\ResourceModel\Repository\ExperienceRepositoryInterface;
  23. use Temando\Shipping\Model\ResourceModel\Repository\ShipmentReferenceRepositoryInterface;
  24. use Temando\Shipping\Model\ResourceModel\Repository\ShipmentRepositoryInterface;
  25. use Temando\Shipping\Model\Shipping\RateResult\FreeShipping;
  26. use Temando\Shipping\Webservice\Processor\OrderQualificationProcessorPool;
  27. /**
  28. * Temando Shipping Carrier
  29. *
  30. * @package Temando\Shipping\Model
  31. * @author Christoph Aßmann <christoph.assmann@netresearch.de>
  32. * @author Sebastian Ertner <sebastian.ertner@netresearch.de>
  33. * @license https://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
  34. * @link https://www.temando.com/
  35. */
  36. class Carrier extends AbstractCarrier implements CarrierInterface
  37. {
  38. /**
  39. * @var StatusFactory
  40. */
  41. private $trackStatusFactory;
  42. /**
  43. * @var MethodFactory
  44. */
  45. private $rateMethodFactory;
  46. /**
  47. * @var ResultFactory
  48. */
  49. private $rateResultFactory;
  50. /**
  51. * @var ManagerInterface
  52. */
  53. private $messageManager;
  54. /**
  55. * @var ShipmentRepositoryInterface
  56. */
  57. private $shipmentRepository;
  58. /**
  59. * @var ShipmentReferenceRepositoryInterface
  60. */
  61. private $shipmentReferenceRepository;
  62. /**
  63. * @var ExperienceRepositoryInterface
  64. */
  65. private $experienceRepository;
  66. /**
  67. * @var QuotingDataInitializer
  68. */
  69. private $quotingDataInitializer;
  70. /**
  71. * @var OrderQualificationProcessorPool
  72. */
  73. private $processorPool;
  74. /**
  75. * @var FreeShipping
  76. */
  77. private $freeShipping;
  78. /**
  79. * Carrier constructor.
  80. * @param ScopeConfigInterface $scopeConfig
  81. * @param ErrorFactory $rateErrorFactory
  82. * @param LoggerInterface $logger
  83. * @param StatusFactory $trackStatusFactory
  84. * @param MethodFactory $rateMethodFactory
  85. * @param ResultFactory $rateResultFactory
  86. * @param ManagerInterface $messageManager
  87. * @param ShipmentRepositoryInterface $shipmentRepository
  88. * @param ShipmentReferenceRepositoryInterface $shipmentReferenceRepository
  89. * @param ExperienceRepositoryInterface $experienceRepository
  90. * @param QuotingDataInitializer $quotingDataInitializer
  91. * @param OrderQualificationProcessorPool $processorPool
  92. * @param FreeShipping $freeShipping
  93. * @param mixed[] $data
  94. */
  95. public function __construct(
  96. ScopeConfigInterface $scopeConfig,
  97. ErrorFactory $rateErrorFactory,
  98. LoggerInterface $logger,
  99. StatusFactory $trackStatusFactory,
  100. MethodFactory $rateMethodFactory,
  101. ResultFactory $rateResultFactory,
  102. ManagerInterface $messageManager,
  103. ShipmentRepositoryInterface $shipmentRepository,
  104. ShipmentReferenceRepositoryInterface $shipmentReferenceRepository,
  105. ExperienceRepositoryInterface $experienceRepository,
  106. QuotingDataInitializer $quotingDataInitializer,
  107. OrderQualificationProcessorPool $processorPool,
  108. FreeShipping $freeShipping,
  109. array $data = []
  110. ) {
  111. $this->trackStatusFactory = $trackStatusFactory;
  112. $this->rateMethodFactory = $rateMethodFactory;
  113. $this->rateResultFactory = $rateResultFactory;
  114. $this->messageManager = $messageManager;
  115. $this->shipmentRepository = $shipmentRepository;
  116. $this->shipmentReferenceRepository = $shipmentReferenceRepository;
  117. $this->experienceRepository = $experienceRepository;
  118. $this->quotingDataInitializer = $quotingDataInitializer;
  119. $this->processorPool = $processorPool;
  120. $this->freeShipping = $freeShipping;
  121. parent::__construct($scopeConfig, $rateErrorFactory, $logger, $data);
  122. }
  123. /**
  124. * Code of the carrier
  125. */
  126. const CODE = 'temando';
  127. /**
  128. * Carrier's code
  129. *
  130. * @var string
  131. */
  132. protected $_code = self::CODE;
  133. /**
  134. * Fetch shipping rates from API.
  135. *
  136. * @param RateRequest $rateRequest
  137. * @return \Magento\Shipping\Model\Rate\Result
  138. */
  139. public function collectRates(RateRequest $rateRequest)
  140. {
  141. $result = $this->rateResultFactory->create();
  142. $activeFlag = $this->getData('active_flag');
  143. if ($activeFlag && !$this->getConfigFlag($activeFlag)) {
  144. return $result;
  145. }
  146. try {
  147. // create request data from rate request
  148. $order = $this->quotingDataInitializer->getOrder($rateRequest);
  149. // send order to Temando platform, will respond with shipping options
  150. $qualifications = $this->experienceRepository->getExperiencesForOrder($order);
  151. // extract applicable shipping options from order qualifications
  152. $shippingOptions = $this->processorPool->processRatesResponse($rateRequest, $order, $qualifications);
  153. } catch (LocalizedException $e) {
  154. $this->_logger->log(LogLevel::WARNING, $e->getMessage(), ['exception' => $e]);
  155. $shippingOptions = [];
  156. }
  157. foreach ($shippingOptions as $shippingOption) {
  158. /** @var \Magento\Quote\Model\Quote\Address\RateResult\Method $method */
  159. $method = $this->rateMethodFactory->create();
  160. $method->setCarrier($this->_code);
  161. $method->setCarrierTitle($this->getConfigData('title'));
  162. $method->setMethod($shippingOption->getCode());
  163. $method->setMethodTitle($shippingOption->getLabel());
  164. $method->setPrice($shippingOption->getCost());
  165. $method->setCost($shippingOption->getCost());
  166. $result->append($method);
  167. }
  168. if (empty($result->getAllRates())) {
  169. /** @var \Magento\Quote\Model\Quote\Address\RateResult\Error $error */
  170. $error = $this->_rateErrorFactory->create(['data' => [
  171. 'carrier' => $this->_code,
  172. 'carrier_title' => $this->getConfigData('title'),
  173. 'error_message' => $this->getConfigData('specificerrmsg'),
  174. ]]);
  175. $result->append($error);
  176. } else {
  177. $this->freeShipping->apply($rateRequest, $result);
  178. }
  179. return $result;
  180. }
  181. /**
  182. * Obtain shipping experiences configured at the merchant account for usage
  183. * in cart price rule conditions.
  184. *
  185. * @return string[]
  186. */
  187. public function getAllowedMethods()
  188. {
  189. $experiences = array_reduce(
  190. $this->experienceRepository->getExperiences(),
  191. function (array $carry, ExperienceInterface $experience) {
  192. if ($experience->getStatus() !== ExperienceInterface::STATUS_DISABLED) {
  193. $carry[$experience->getExperienceId()] = $experience->getName();
  194. }
  195. return $carry;
  196. },
  197. []
  198. );
  199. asort($experiences);
  200. return $experiences;
  201. }
  202. /**
  203. * @return bool
  204. */
  205. public function isTrackingAvailable()
  206. {
  207. return true;
  208. }
  209. /**
  210. * Get tracking information. Original return value annotation is misleading.
  211. *
  212. * @see \Magento\Shipping\Model\Carrier\AbstractCarrier::isTrackingAvailable()
  213. * @see \Magento\Shipping\Model\Carrier\AbstractCarrierOnline::getTrackingInfo()
  214. * @see \Magento\Dhl\Model\Carrier::getTracking()
  215. * @param string $trackingNumber
  216. * @return \Magento\Shipping\Model\Tracking\Result\AbstractResult
  217. */
  218. public function getTrackingInfo($trackingNumber)
  219. {
  220. /** @var \Magento\Shipping\Model\Tracking\Result\Status $tracking */
  221. $tracking = $this->trackStatusFactory->create();
  222. $tracking->setCarrier($this->_code);
  223. $tracking->setTracking($trackingNumber);
  224. try {
  225. $shipmentTrack = $this->shipmentReferenceRepository->getShipmentTrack($this->_code, $trackingNumber);
  226. $carrierTitle = $shipmentTrack->getTitle() ? $shipmentTrack->getTitle() : $this->getConfigData('title');
  227. $trackingUrl = $this->getTrackingUrl($shipmentTrack);
  228. $tracking->setCarrierTitle($carrierTitle);
  229. $tracking->setUrl($trackingUrl);
  230. } catch (LocalizedException $exception) {
  231. $this->messageManager->addErrorMessage($exception->getMessage());
  232. $tracking->setCarrierTitle($this->getConfigData('title'));
  233. $tracking->setUrl('');
  234. }
  235. return $tracking;
  236. }
  237. /**
  238. * Extract tracking URL from platform shipment. If possible, read url from package level.
  239. *
  240. * @param ShipmentTrackInterface $shipmentTrack
  241. * @return string
  242. * @throws NoSuchEntityException
  243. * @throws LocalizedException
  244. */
  245. private function getTrackingUrl(ShipmentTrackInterface $shipmentTrack)
  246. {
  247. $shipmentId = $shipmentTrack->getParentId();
  248. $shipmentReference = $this->shipmentReferenceRepository->getByShipmentId($shipmentId);
  249. $shipment = $this->shipmentRepository->getById($shipmentReference->getExtShipmentId());
  250. // read tracking url from booking fulfillment
  251. $trackingUrl = $shipment->getFulfillment()->getTrackingUrl();
  252. // check if there is a matching tracking url in the packages
  253. foreach ($shipment->getPackages() as $package) {
  254. if (!$package->getTrackingUrl()) {
  255. continue;
  256. }
  257. if ($package->getTrackingReference() === $shipmentTrack->getTrackNumber()) {
  258. $trackingUrl = $package->getTrackingUrl();
  259. }
  260. }
  261. return (string) $trackingUrl;
  262. }
  263. }