Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
Shipping.php
Go to the documentation of this file.
1 <?php
6 namespace Magento\Shipping\Model;
7 
10 use Magento\Quote\Model\Quote\Address\RateRequestFactory;
12 
17 {
23  protected $_orig = null;
24 
30  protected $_result = null;
31 
37  protected $_availabilityConfigField = 'active';
38 
44  protected $_scopeConfig;
45 
49  protected $_storeManager;
50 
54  protected $_shippingConfig;
55 
59  protected $_carrierFactory;
60 
65 
70 
74  protected $_regionFactory;
75 
79  protected $mathDivision;
80 
84  protected $stockRegistry;
85 
89  private $rateRequestFactory;
90 
105  public function __construct(
106  \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
107  \Magento\Shipping\Model\Config $shippingConfig,
108  \Magento\Store\Model\StoreManagerInterface $storeManager,
109  \Magento\Shipping\Model\CarrierFactory $carrierFactory,
110  \Magento\Shipping\Model\Rate\ResultFactory $rateResultFactory,
111  \Magento\Shipping\Model\Shipment\RequestFactory $shipmentRequestFactory,
112  \Magento\Directory\Model\RegionFactory $regionFactory,
113  \Magento\Framework\Math\Division $mathDivision,
114  \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry,
115  RateRequestFactory $rateRequestFactory = null
116  ) {
117  $this->_scopeConfig = $scopeConfig;
118  $this->_shippingConfig = $shippingConfig;
119  $this->_storeManager = $storeManager;
120  $this->_carrierFactory = $carrierFactory;
121  $this->_rateResultFactory = $rateResultFactory;
122  $this->_shipmentRequestFactory = $shipmentRequestFactory;
123  $this->_regionFactory = $regionFactory;
124  $this->mathDivision = $mathDivision;
125  $this->stockRegistry = $stockRegistry;
126  $this->rateRequestFactory = $rateRequestFactory ?: ObjectManager::getInstance()->get(RateRequestFactory::class);
127  }
128 
134  public function getResult()
135  {
136  if (empty($this->_result)) {
137  $this->_result = $this->_rateResultFactory->create();
138  }
139  return $this->_result;
140  }
141 
148  public function setOrigData($data)
149  {
150  $this->_orig = $data;
151  }
152 
158  public function resetResult()
159  {
160  $this->getResult()->reset();
161  return $this;
162  }
163 
169  public function getConfig()
170  {
171  return $this->_shippingConfig;
172  }
173 
181  public function collectRates(\Magento\Quote\Model\Quote\Address\RateRequest $request)
182  {
183  $storeId = $request->getStoreId();
184  if (!$request->getOrig()) {
185  $request->setCountryId(
186  $this->_scopeConfig->getValue(
188  \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
189  $request->getStore()
190  )
191  )->setRegionId(
192  $this->_scopeConfig->getValue(
194  \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
195  $request->getStore()
196  )
197  )->setCity(
198  $this->_scopeConfig->getValue(
200  \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
201  $request->getStore()
202  )
203  )->setPostcode(
204  $this->_scopeConfig->getValue(
206  \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
207  $request->getStore()
208  )
209  );
210  }
211 
212  $limitCarrier = $request->getLimitCarrier();
213  if (!$limitCarrier) {
214  $carriers = $this->_scopeConfig->getValue(
215  'carriers',
216  \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
217  $storeId
218  );
219 
220  foreach ($carriers as $carrierCode => $carrierConfig) {
221  $this->collectCarrierRates($carrierCode, $request);
222  }
223  } else {
224  if (!is_array($limitCarrier)) {
225  $limitCarrier = [$limitCarrier];
226  }
227  foreach ($limitCarrier as $carrierCode) {
228  $carrierConfig = $this->_scopeConfig->getValue(
229  'carriers/' . $carrierCode,
230  \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
231  $storeId
232  );
233  if (!$carrierConfig) {
234  continue;
235  }
236  $this->collectCarrierRates($carrierCode, $request);
237  }
238  }
239 
240  return $this;
241  }
242 
252  public function collectCarrierRates($carrierCode, $request)
253  {
254  /* @var $carrier \Magento\Shipping\Model\Carrier\AbstractCarrier */
255  $carrier = $this->_carrierFactory->createIfActive($carrierCode, $request->getStoreId());
256  if (!$carrier) {
257  return $this;
258  }
259  $carrier->setActiveFlag($this->_availabilityConfigField);
260  $result = $carrier->checkAvailableShipCountries($request);
261  if (false !== $result && !$result instanceof \Magento\Quote\Model\Quote\Address\RateResult\Error) {
262  $result = $carrier->processAdditionalValidation($request);
263  }
264  /*
265  * Result will be false if the admin set not to show the shipping module
266  * if the delivery country is not within specific countries
267  */
268  if (false !== $result) {
269  if (!$result instanceof \Magento\Quote\Model\Quote\Address\RateResult\Error) {
270  if ($carrier->getConfigData('shipment_requesttype')) {
271  $packages = $this->composePackagesForCarrier($carrier, $request);
272  if (!empty($packages)) {
273  $sumResults = [];
274  foreach ($packages as $weight => $packageCount) {
275  $request->setPackageWeight($weight);
276  $result = $carrier->collectRates($request);
277  if (!$result) {
278  return $this;
279  } else {
280  $result->updateRatePrice($packageCount);
281  }
282  $sumResults[] = $result;
283  }
284  if (!empty($sumResults) && count($sumResults) > 1) {
285  $result = [];
286  foreach ($sumResults as $res) {
287  if (empty($result)) {
288  $result = $res;
289  continue;
290  }
291  foreach ($res->getAllRates() as $method) {
292  foreach ($result->getAllRates() as $resultMethod) {
293  if ($method->getMethod() == $resultMethod->getMethod()) {
294  $resultMethod->setPrice($method->getPrice() + $resultMethod->getPrice());
295  continue;
296  }
297  }
298  }
299  }
300  }
301  } else {
302  $result = $carrier->collectRates($request);
303  }
304  } else {
305  $result = $carrier->collectRates($request);
306  }
307  if (!$result) {
308  return $this;
309  }
310  }
311  if ($carrier->getConfigData('showmethod') == 0 && $result->getError()) {
312  return $this;
313  }
314  // sort rates by price
315  if (method_exists($result, 'sortRatesByPrice') && is_callable([$result, 'sortRatesByPrice'])) {
316  $result->sortRatesByPrice();
317  }
318  $this->getResult()->append($result);
319  }
320  return $this;
321  }
322 
333  public function composePackagesForCarrier($carrier, $request)
334  {
335  $allItems = $request->getAllItems();
336  $fullItems = [];
337 
338  $maxWeight = (double)$carrier->getConfigData('max_package_weight');
339 
341  foreach ($allItems as $item) {
342  if ($item->getProductType() == \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE
343  && $item->getProduct()->getShipmentType()
344  ) {
345  continue;
346  }
347 
348  $qty = $item->getQty();
349  $changeQty = true;
350  $checkWeight = true;
351  $decimalItems = [];
352 
353  if ($item->getParentItem()) {
354  if (!$item->getParentItem()->getProduct()->getShipmentType()) {
355  continue;
356  }
357  $qty = $item->getIsQtyDecimal()
358  ? $item->getParentItem()->getQty()
359  : $item->getParentItem()->getQty() * $item->getQty();
360  }
361 
362  $itemWeight = $item->getWeight();
363  if ($item->getIsQtyDecimal()
364  && $item->getProductType() != \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE
365  ) {
366  $productId = $item->getProduct()->getId();
367 
368  $stockItem = $this->stockRegistry->getStockItem($productId, $item->getStore()->getWebsiteId());
369  if ($stockItem->getIsDecimalDivided()) {
370  if ($stockItem->getEnableQtyIncrements() && $stockItem->getQtyIncrements()) {
371  $itemWeight = $itemWeight * $stockItem->getQtyIncrements();
372  $qty = round($item->getWeight() / $itemWeight * $qty);
373  $changeQty = false;
374  } else {
375  $itemWeight = $itemWeight * $item->getQty();
376  if ($itemWeight > $maxWeight) {
377  $qtyItem = floor($itemWeight / $maxWeight);
378  $decimalItems[] = ['weight' => $maxWeight, 'qty' => $qtyItem];
379  $weightItem = $this->mathDivision->getExactDivision($itemWeight, $maxWeight);
380  if ($weightItem) {
381  $decimalItems[] = ['weight' => $weightItem, 'qty' => 1];
382  }
383  $checkWeight = false;
384  } else {
385  $itemWeight = $itemWeight * $item->getQty();
386  }
387  }
388  } else {
389  $itemWeight = $itemWeight * $item->getQty();
390  }
391  }
392 
393  if ($checkWeight && $maxWeight && $itemWeight > $maxWeight) {
394  return [];
395  }
396 
397  if ($changeQty
398  && !$item->getParentItem()
399  && $item->getIsQtyDecimal()
400  && $item->getProductType() != \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE
401  ) {
402  $qty = 1;
403  }
404 
405  if (!empty($decimalItems)) {
406  foreach ($decimalItems as $decimalItem) {
407  $fullItems = array_merge(
408  $fullItems,
409  array_fill(0, $decimalItem['qty'] * $qty, $decimalItem['weight'])
410  );
411  }
412  } else {
413  $fullItems = array_merge($fullItems, array_fill(0, $qty, $itemWeight));
414  }
415  }
416  sort($fullItems);
417 
418  return $this->_makePieces($fullItems, $maxWeight);
419  }
420 
429  protected function _makePieces($items, $maxWeight)
430  {
431  $pieces = [];
432  if (!empty($items)) {
433  $sumWeight = 0;
434 
435  $reverseOrderItems = $items;
436  arsort($reverseOrderItems);
437 
438  foreach ($reverseOrderItems as $key => $weight) {
439  if (!isset($items[$key])) {
440  continue;
441  }
442  unset($items[$key]);
443  $sumWeight = $weight;
444  foreach ($items as $key => $weight) {
445  if ($sumWeight + $weight < $maxWeight) {
446  unset($items[$key]);
447  $sumWeight += $weight;
448  } elseif ($sumWeight + $weight > $maxWeight) {
449  $pieces[] = (string)(double)$sumWeight;
450  break;
451  } else {
452  unset($items[$key]);
453  $pieces[] = (string)(double)($sumWeight + $weight);
454  $sumWeight = 0;
455  break;
456  }
457  }
458  }
459  if ($sumWeight > 0) {
460  $pieces[] = (string)(double)$sumWeight;
461  }
462  $pieces = array_count_values($pieces);
463  }
464 
465  return $pieces;
466  }
467 
475  public function collectRatesByAddress(\Magento\Framework\DataObject $address, $limitCarrier = null)
476  {
478  $request = $this->rateRequestFactory->create();
479  $request->setAllItems($address->getAllItems());
480  $request->setDestCountryId($address->getCountryId());
481  $request->setDestRegionId($address->getRegionId());
482  $request->setDestPostcode($address->getPostcode());
483  $request->setPackageValue($address->getBaseSubtotal());
484  $request->setPackageValueWithDiscount($address->getBaseSubtotalWithDiscount());
485  $request->setPackageWeight($address->getWeight());
486  $request->setFreeMethodWeight($address->getFreeMethodWeight());
487  $request->setPackageQty($address->getItemQty());
488 
490  $store = $this->_storeManager->getStore();
491  $request->setStoreId($store->getId());
492  $request->setWebsiteId($store->getWebsiteId());
493  $request->setBaseCurrency($store->getBaseCurrency());
494  $request->setPackageCurrency($store->getCurrentCurrency());
495  $request->setLimitCarrier($limitCarrier);
496 
497  $request->setBaseSubtotalInclTax($address->getBaseSubtotalInclTax());
498 
499  return $this->collectRates($request);
500  }
501 
508  public function setCarrierAvailabilityConfigField($code = 'active')
509  {
510  $this->_availabilityConfigField = $code;
511  return $this;
512  }
513 }
setCarrierAvailabilityConfigField($code='active')
Definition: Shipping.php:508
elseif(isset( $params[ 'redirect_parent']))
Definition: iframe.phtml:17
collectRates(\Magento\Quote\Model\Quote\Address\RateRequest $request)
Definition: Shipping.php:181
__construct(\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, \Magento\Shipping\Model\Config $shippingConfig, \Magento\Store\Model\StoreManagerInterface $storeManager, \Magento\Shipping\Model\CarrierFactory $carrierFactory, \Magento\Shipping\Model\Rate\ResultFactory $rateResultFactory, \Magento\Shipping\Model\Shipment\RequestFactory $shipmentRequestFactory, \Magento\Directory\Model\RegionFactory $regionFactory, \Magento\Framework\Math\Division $mathDivision, \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry, RateRequestFactory $rateRequestFactory=null)
Definition: Shipping.php:105
collectCarrierRates($carrierCode, $request)
Definition: Shipping.php:252
$storeManager
_makePieces($items, $maxWeight)
Definition: Shipping.php:429
$address
Definition: customer.php:38
$method
Definition: info.phtml:13
$code
Definition: info.phtml:12
$items