quoteRepository = $quoteRepository; $this->converter = $converter; $this->addressRepository = $addressRepository; $this->totalsCollector = $totalsCollector; $this->addressFactory = $addressFactory ?: ObjectManager::getInstance() ->get(AddressInterfaceFactory::class); $this->quoteAddressResource = $quoteAddressResource ?: ObjectManager::getInstance() ->get(QuoteAddressResource::class); $this->customerSession = $customerSession ?? ObjectManager::getInstance()->get(CustomerSession::class); } /** * @inheritDoc */ public function get($cartId) { /** @var Quote $quote */ $quote = $this->quoteRepository->getActive($cartId); /** @var Address $shippingAddress */ $shippingAddress = $quote->getShippingAddress(); if (!$shippingAddress->getCountryId()) { throw new StateException(__('The shipping address is missing. Set the address and try again.')); } $shippingMethod = $shippingAddress->getShippingMethod(); if (!$shippingMethod) { return null; } $shippingAddress->collectShippingRates(); /** @var Rate $shippingRate */ $shippingRate = $shippingAddress->getShippingRateByCode($shippingMethod); if (!$shippingRate) { return null; } return $this->converter->modelToDataObject($shippingRate, $quote->getQuoteCurrencyCode()); } /** * @inheritDoc */ public function getList($cartId) { $output = []; /** @var Quote $quote */ $quote = $this->quoteRepository->getActive($cartId); // no methods applicable for empty carts or carts with virtual products if ($quote->isVirtual() || 0 == $quote->getItemsCount()) { return []; } $shippingAddress = $quote->getShippingAddress(); if (!$shippingAddress->getCountryId()) { throw new StateException(__('The shipping address is missing. Set the address and try again.')); } $shippingAddress->collectShippingRates(); $shippingRates = $shippingAddress->getGroupedAllShippingRates(); foreach ($shippingRates as $carrierRates) { foreach ($carrierRates as $rate) { $output[] = $this->converter->modelToDataObject($rate, $quote->getQuoteCurrencyCode()); } } return $output; } /** * @inheritDoc */ public function set($cartId, $carrierCode, $methodCode) { /** @var Quote $quote */ $quote = $this->quoteRepository->getActive($cartId); try { $this->apply($cartId, $carrierCode, $methodCode); } catch (\Exception $e) { throw $e; } try { $this->quoteRepository->save($quote->collectTotals()); } catch (\Exception $e) { throw new CouldNotSaveException(__('The shipping method can\'t be set. %1', $e->getMessage())); } return true; } /** * Apply carrier code. * * @param int $cartId The shopping cart ID. * @param string $carrierCode The carrier code. * @param string $methodCode The shipping method code. * @return void * @throws InputException The shipping method is not valid for an empty cart. * @throws NoSuchEntityException CThe Cart includes virtual product(s) only, so a shipping address is not used. * @throws StateException The billing or shipping address is not set. * @throws \Exception */ public function apply($cartId, $carrierCode, $methodCode) { /** @var Quote $quote */ $quote = $this->quoteRepository->getActive($cartId); if (0 == $quote->getItemsCount()) { throw new InputException( __('The shipping method can\'t be set for an empty cart. Add an item to cart and try again.') ); } if ($quote->isVirtual()) { throw new NoSuchEntityException( __('The Cart includes virtual product(s) only, so a shipping address is not used.') ); } $shippingAddress = $quote->getShippingAddress(); if (!$shippingAddress->getCountryId()) { // Remove empty quote address $this->quoteAddressResource->delete($shippingAddress); throw new StateException(__('The shipping address is missing. Set the address and try again.')); } $shippingMethod = $carrierCode . '_' . $methodCode; $shippingAddress->setShippingMethod($shippingMethod); $shippingAssignments = (array)$quote->getExtensionAttributes()->getShippingAssignments(); if (!empty($shippingAssignments)) { $shippingAssignment = $shippingAssignments[0]; $shipping = $shippingAssignment->getShipping(); $shipping->setMethod($shippingMethod); $shippingAssignment->setShipping($shipping); } } /** * @inheritDoc */ public function estimateByAddress($cartId, EstimateAddressInterface $address) { /** @var Quote $quote */ $quote = $this->quoteRepository->getActive($cartId); // no methods applicable for empty carts or carts with virtual products if ($quote->isVirtual() || 0 == $quote->getItemsCount()) { return []; } return $this->getShippingMethods($quote, $address); } /** * @inheritdoc */ public function estimateByExtendedAddress($cartId, AddressInterface $address) { /** @var Quote $quote */ $quote = $this->quoteRepository->getActive($cartId); // no methods applicable for empty carts or carts with virtual products if ($quote->isVirtual() || 0 == $quote->getItemsCount()) { return []; } return $this->getShippingMethods($quote, $address); } /** * @inheritDoc * @throws InputException * @throws NoSuchEntityException */ public function estimateByAddressId($cartId, $addressId) { /** @var Quote $quote */ $quote = $this->quoteRepository->getActive($cartId); // no methods applicable for empty carts or carts with virtual products if ($quote->isVirtual() || 0 == $quote->getItemsCount()) { return []; } $address = $this->getAddress($addressId, $quote); return $this->getShippingMethods($quote, $address); } /** * Get estimated rates * * @param Quote $quote * @param int $country * @param string $postcode * @param int $regionId * @param string $region * @param ExtensibleDataInterface|null $address * @return ShippingMethodInterface[] An array of shipping methods. * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @deprecated 100.1.6 * @see Updated-deprication-doc-annotations */ protected function getEstimatedRates( Quote $quote, $country, $postcode, $regionId, $region, $address = null ) { if (!$address) { $address = $this->addressFactory->create() ->setCountryId($country) ->setPostcode($postcode) ->setRegionId($regionId); } return $this->getShippingMethods($quote, $address); } /** * Get list of available shipping methods * * @param Quote $quote * @param ExtensibleDataInterface $address * @return ShippingMethodInterface[] */ private function getShippingMethods(Quote $quote, $address) { $output = []; $shippingAddress = $quote->getShippingAddress(); $shippingAddress->addData($this->extractAddressData($address)); $shippingAddress->setCollectShippingRates(true); $this->totalsCollector->collectAddressTotals($quote, $shippingAddress); $quoteCustomerGroupId = $quote->getCustomerGroupId(); $customerGroupId = $this->customerSession->getCustomerGroupId(); $isCustomerGroupChanged = $quoteCustomerGroupId !== $customerGroupId; if ($isCustomerGroupChanged) { $quote->setCustomerGroupId($customerGroupId); } $shippingRates = $shippingAddress->getGroupedAllShippingRates(); foreach ($shippingRates as $carrierRates) { foreach ($carrierRates as $rate) { $output[] = $this->converter->modelToDataObject($rate, $quote->getQuoteCurrencyCode()); } } if ($isCustomerGroupChanged) { $quote->setCustomerGroupId($quoteCustomerGroupId); } return $output; } /** * Get transform address interface into Array * * @param ExtensibleDataInterface $address * @return array */ private function extractAddressData($address) { $className = \Magento\Customer\Api\Data\AddressInterface::class; if ($address instanceof AddressInterface) { $className = AddressInterface::class; } elseif ($address instanceof EstimateAddressInterface) { $className = EstimateAddressInterface::class; } $addressData = $this->getDataObjectProcessor()->buildOutputDataArray( $address, $className ); unset($addressData[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]); return $addressData; } /** * Gets the data object processor * * @return DataObjectProcessor * @deprecated 101.0.0 * @see Updated-deprication-doc-annotations */ private function getDataObjectProcessor() { if ($this->dataProcessor === null) { $this->dataProcessor = ObjectManager::getInstance() ->get(DataObjectProcessor::class); } return $this->dataProcessor; } /** * Gets the address if exists for customer * * @param int $addressId * @param Quote $quote * @return CustomerAddress * @throws InputException The shipping address is incorrect. */ private function getAddress(int $addressId, Quote $quote): CustomerAddress { $addresses = $quote->getCustomer()->getAddresses(); foreach ($addresses as $address) { if ($addressId === (int)$address->getId()) { return $address; } } throw new InputException(__('The shipping address is missing. Set the address and try again.')); } }