Tablerate.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\OfflineShipping\Model\Carrier;
  7. use Magento\Framework\Exception\LocalizedException;
  8. use Magento\Quote\Model\Quote\Address\RateRequest;
  9. /**
  10. * Table rate shipping model
  11. *
  12. * @api
  13. * @since 100.0.2
  14. */
  15. class Tablerate extends \Magento\Shipping\Model\Carrier\AbstractCarrier implements
  16. \Magento\Shipping\Model\Carrier\CarrierInterface
  17. {
  18. /**
  19. * @var string
  20. */
  21. protected $_code = 'tablerate';
  22. /**
  23. * @var bool
  24. */
  25. protected $_isFixed = true;
  26. /**
  27. * @var string
  28. */
  29. protected $_defaultConditionName = 'package_weight';
  30. /**
  31. * @var array
  32. */
  33. protected $_conditionNames = [];
  34. /**
  35. * @var \Magento\Shipping\Model\Rate\ResultFactory
  36. */
  37. protected $_rateResultFactory;
  38. /**
  39. * @var \Magento\Quote\Model\Quote\Address\RateResult\MethodFactory
  40. */
  41. protected $_resultMethodFactory;
  42. /**
  43. * @var \Magento\OfflineShipping\Model\ResourceModel\Carrier\TablerateFactory
  44. */
  45. protected $_tablerateFactory;
  46. /**
  47. * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
  48. * @param \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory
  49. * @param \Psr\Log\LoggerInterface $logger
  50. * @param \Magento\Shipping\Model\Rate\ResultFactory $rateResultFactory
  51. * @param \Magento\Quote\Model\Quote\Address\RateResult\MethodFactory $resultMethodFactory
  52. * @param \Magento\OfflineShipping\Model\ResourceModel\Carrier\TablerateFactory $tablerateFactory
  53. * @param array $data
  54. * @throws LocalizedException
  55. * @SuppressWarnings(PHPMD.UnusedLocalVariable)
  56. */
  57. public function __construct(
  58. \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
  59. \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory,
  60. \Psr\Log\LoggerInterface $logger,
  61. \Magento\Shipping\Model\Rate\ResultFactory $rateResultFactory,
  62. \Magento\Quote\Model\Quote\Address\RateResult\MethodFactory $resultMethodFactory,
  63. \Magento\OfflineShipping\Model\ResourceModel\Carrier\TablerateFactory $tablerateFactory,
  64. array $data = []
  65. ) {
  66. $this->_rateResultFactory = $rateResultFactory;
  67. $this->_resultMethodFactory = $resultMethodFactory;
  68. $this->_tablerateFactory = $tablerateFactory;
  69. parent::__construct($scopeConfig, $rateErrorFactory, $logger, $data);
  70. foreach ($this->getCode('condition_name') as $k => $v) {
  71. $this->_conditionNames[] = $k;
  72. }
  73. }
  74. /**
  75. * Collect rates.
  76. *
  77. * @param RateRequest $request
  78. * @return \Magento\Shipping\Model\Rate\Result
  79. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  80. * @SuppressWarnings(PHPMD.NPathComplexity)
  81. * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
  82. */
  83. public function collectRates(RateRequest $request)
  84. {
  85. if (!$this->getConfigFlag('active')) {
  86. return false;
  87. }
  88. // exclude Virtual products price from Package value if pre-configured
  89. if (!$this->getConfigFlag('include_virtual_price') && $request->getAllItems()) {
  90. foreach ($request->getAllItems() as $item) {
  91. if ($item->getParentItem()) {
  92. continue;
  93. }
  94. if ($item->getHasChildren() && $item->isShipSeparately()) {
  95. foreach ($item->getChildren() as $child) {
  96. if ($child->getProduct()->isVirtual()) {
  97. $request->setPackageValue($request->getPackageValue() - $child->getBaseRowTotal());
  98. }
  99. }
  100. } elseif ($item->getProduct()->isVirtual()) {
  101. $request->setPackageValue($request->getPackageValue() - $item->getBaseRowTotal());
  102. }
  103. }
  104. }
  105. // Free shipping by qty
  106. $freeQty = 0;
  107. $freePackageValue = 0;
  108. if ($request->getAllItems()) {
  109. foreach ($request->getAllItems() as $item) {
  110. if ($item->getProduct()->isVirtual() || $item->getParentItem()) {
  111. continue;
  112. }
  113. if ($item->getHasChildren() && $item->isShipSeparately()) {
  114. foreach ($item->getChildren() as $child) {
  115. if ($child->getFreeShipping() && !$child->getProduct()->isVirtual()) {
  116. $freeShipping = is_numeric($child->getFreeShipping()) ? $child->getFreeShipping() : 0;
  117. $freeQty += $item->getQty() * ($child->getQty() - $freeShipping);
  118. }
  119. }
  120. } elseif ($item->getFreeShipping() || $item->getAddress()->getFreeShipping()) {
  121. $freeShipping = $item->getFreeShipping() ?
  122. $item->getFreeShipping() : $item->getAddress()->getFreeShipping();
  123. $freeShipping = is_numeric($freeShipping) ? $freeShipping : 0;
  124. $freeQty += $item->getQty() - $freeShipping;
  125. $freePackageValue += $item->getBaseRowTotal();
  126. }
  127. }
  128. $oldValue = $request->getPackageValue();
  129. $request->setPackageValue($oldValue - $freePackageValue);
  130. }
  131. if (!$request->getConditionName()) {
  132. $conditionName = $this->getConfigData('condition_name');
  133. $request->setConditionName($conditionName ? $conditionName : $this->_defaultConditionName);
  134. }
  135. // Package weight and qty free shipping
  136. $oldWeight = $request->getPackageWeight();
  137. $oldQty = $request->getPackageQty();
  138. $request->setPackageWeight($request->getFreeMethodWeight());
  139. $request->setPackageQty($oldQty - $freeQty);
  140. /** @var \Magento\Shipping\Model\Rate\Result $result */
  141. $result = $this->_rateResultFactory->create();
  142. $rate = $this->getRate($request);
  143. $request->setPackageWeight($oldWeight);
  144. $request->setPackageQty($oldQty);
  145. if (!empty($rate) && $rate['price'] >= 0) {
  146. if ($request->getPackageQty() == $freeQty) {
  147. $shippingPrice = 0;
  148. } else {
  149. $shippingPrice = $this->getFinalPriceWithHandlingFee($rate['price']);
  150. }
  151. $method = $this->createShippingMethod($shippingPrice, $rate['cost']);
  152. $result->append($method);
  153. } elseif ($request->getPackageQty() == $freeQty) {
  154. /**
  155. * Promotion rule was applied for the whole cart.
  156. * In this case all other shipping methods could be omitted
  157. * Table rate shipping method with 0$ price must be shown if grand total is more than minimal value.
  158. * Free package weight has been already taken into account.
  159. */
  160. $request->setPackageValue($freePackageValue);
  161. $request->setPackageQty($freeQty);
  162. $rate = $this->getRate($request);
  163. if (!empty($rate) && $rate['price'] >= 0) {
  164. $method = $this->createShippingMethod(0, 0);
  165. $result->append($method);
  166. }
  167. } else {
  168. /** @var \Magento\Quote\Model\Quote\Address\RateResult\Error $error */
  169. $error = $this->_rateErrorFactory->create(
  170. [
  171. 'data' => [
  172. 'carrier' => $this->_code,
  173. 'carrier_title' => $this->getConfigData('title'),
  174. 'error_message' => $this->getConfigData('specificerrmsg'),
  175. ],
  176. ]
  177. );
  178. $result->append($error);
  179. }
  180. return $result;
  181. }
  182. /**
  183. * Get rate.
  184. *
  185. * @param \Magento\Quote\Model\Quote\Address\RateRequest $request
  186. * @return array|bool
  187. */
  188. public function getRate(\Magento\Quote\Model\Quote\Address\RateRequest $request)
  189. {
  190. return $this->_tablerateFactory->create()->getRate($request);
  191. }
  192. /**
  193. * Get code.
  194. *
  195. * @param string $type
  196. * @param string $code
  197. * @return array
  198. * @throws \Magento\Framework\Exception\LocalizedException
  199. */
  200. public function getCode($type, $code = '')
  201. {
  202. $codes = [
  203. 'condition_name' => [
  204. 'package_weight' => __('Weight vs. Destination'),
  205. 'package_value_with_discount' => __('Price vs. Destination'),
  206. 'package_qty' => __('# of Items vs. Destination'),
  207. ],
  208. 'condition_name_short' => [
  209. 'package_weight' => __('Weight (and above)'),
  210. 'package_value_with_discount' => __('Order Subtotal (and above)'),
  211. 'package_qty' => __('# of Items (and above)'),
  212. ],
  213. ];
  214. if (!isset($codes[$type])) {
  215. throw new LocalizedException(
  216. __('The "%1" code type for Table Rate is incorrect. Verify the type and try again.', $type)
  217. );
  218. }
  219. if ('' === $code) {
  220. return $codes[$type];
  221. }
  222. if (!isset($codes[$type][$code])) {
  223. throw new LocalizedException(
  224. __('The "%1: %2" code type for Table Rate is incorrect. Verify the type and try again.', $type, $code)
  225. );
  226. }
  227. return $codes[$type][$code];
  228. }
  229. /**
  230. * Get allowed shipping methods
  231. *
  232. * @return array
  233. */
  234. public function getAllowedMethods()
  235. {
  236. return ['bestway' => $this->getConfigData('name')];
  237. }
  238. /**
  239. * Get the method object based on the shipping price and cost
  240. *
  241. * @param float $shippingPrice
  242. * @param float $cost
  243. * @return \Magento\Quote\Model\Quote\Address\RateResult\Method
  244. */
  245. private function createShippingMethod($shippingPrice, $cost)
  246. {
  247. /** @var \Magento\Quote\Model\Quote\Address\RateResult\Method $method */
  248. $method = $this->_resultMethodFactory->create();
  249. $method->setCarrier('tablerate');
  250. $method->setCarrierTitle($this->getConfigData('title'));
  251. $method->setMethod('bestway');
  252. $method->setMethodTitle($this->getConfigData('name'));
  253. $method->setPrice($shippingPrice);
  254. $method->setCost($cost);
  255. return $method;
  256. }
  257. }