1

I want to display Current product shipping rate on the product detail page.

I have tried below code,

$qty = 1;
        $countryId='IN';
        $storeId = 1;
        $zipcode = 360002;
        $product = $this->_catalogProduct->load($id);
        $item = $objectManager->create('Magento\Quote\Model\Quote\Item')->setProduct($product)->setQty(1);
        $store = $objectManager->create('Magento\Store\Model\Store')->load($storeId);
        $request = $objectManager->create('Magento\Quote\Model\Quote\Address\RateRequest')->setAllItems(array($item))
            ->setDestCountryId($country)
            ->setPackageValue($product->getFinalPrice())
            ->setOrigPostcode($zipcode)  
            ->setPackageValueWithDiscount($product->getFinalPrice())
            ->setPackageWeight($product->getWeight())
            ->setPackageQty(1)
            ->setPackagePhysicalValue($product->getFinalPrice())
            ->setFreeMethodWeight(0)
            ->setStoreId($store->getId())
            ->setWebsiteId($store->getWebsiteId())
            ->setFreeShipping(0)
            ->setBaseCurrency($store->getBaseCurrency())
            ->setBaseSubtotalInclTax($product->getFinalPrice());
         $result = $this->_rateCollector->create()->collectRates($request)->getResult();

I want to display more than one enabled shipping methods rates in product display page. I have tried this code but i am getting this kind of error.

Uncaught Error: Call to a member function getItemsQty() on null in/vendor/magento/module-quote/Model/Quote/Item.php:275

Please help me on this.

3

Although Manish's answer does show how to create a block on the product page to obtain the available shipping rates, the REST API used in the tutorial (rest/default/V1/carts/mine/estimate-shipping-methods) only fetches this for the items that are already in the cart, and not just for the currently viewed product. This is because Magento relies on items that are added to the active quote to obtain the available methods and costs, of which was fetched from the database and utilised. Despite this, we can create a blank quote in memory without saving it into the database, fill in the required data, and then apply this to the shipping calculator.

The best approach to this is to create a new API endpoint, which you can then swap out the endpoint defined in the tutorial with yours.

Interface

<?php

namespace Your\Module\Api;

use Magento\Quote\Api\Data\AddressInterface;

/**
 * Interface ShipmentForProductEstimationInterface
 * @api
 * @since 100.0.7
 */
interface ShipmentForProductEstimationInterface
{
    /**
     * Estimate shipping by address and product, and return list of available shipping methods
     * @param string $sku
     * @param AddressInterface $address
     * @return \Magento\Quote\Api\Data\ShippingMethodInterface[] An array of shipping methods
     */
    public function estimateByProductAndAddress($sku, AddressInterface $address);
}

Model

<?php

namespace Your\Module\Model\Api;

use Magento\Framework\App\ObjectManager;

class ShipmentForProductEstimation implements \Your\Module\Api\ShipmentForProductEstimationInterface {

    /**
     * Shipping method converter
     *
     * @var \Magento\Quote\Model\Cart\ShippingMethodConverter
     */
    protected $converter;

    /**
     * @var \Magento\Quote\Model\Quote\TotalsCollector
     */
    protected $totalsCollector;

    /**
     * @var \Magento\Framework\Reflection\DataObjectProcessor
     */
    protected $dataProcessor;

    /**
     * @var \Magento\Quote\Model\Quote
     */
    protected $quote;

    /**
     * @var \Magento\Catalog\Api\ProductRepositoryInterface
     */
    protected $product;

    /**
     * @var \Magento\Quote\Model\Quote\Item
     */
    protected $item;

    public function __construct(
        \Magento\Quote\Model\Cart\ShippingMethodConverter $converter,
        \Magento\Quote\Model\Quote\TotalsCollector $totalsCollector,
        \Magento\Quote\Model\Quote $quote,
        \Magento\Catalog\Api\ProductRepositoryInterface $product,
        \Magento\Quote\Model\Quote\Item $item
    ) {
        $this->converter = $converter;
        $this->totalsCollector = $totalsCollector;
        $this->quote = $quote;
        $this->product = $product;
        $this->item = $item;
    }

    /**
     * @inheritdoc
     */
    public function estimateByProductAndAddress($sku, \Magento\Quote\Api\Data\AddressInterface $address)
    {
        $output = [];

        $address->setCountryId('IN');

        $p = $this->product->get($sku);

        $this->item->setProduct($p);
        $this->item->setQty(1);

        $this->quote->addItem( $this->item );

        $shippingAddress = $this->quote->getShippingAddress();
        $shippingAddress->addData($this->extractAddressData($address));
        $shippingAddress->setCollectShippingRates(true);

        $this->totalsCollector->collectAddressTotals($this->quote, $shippingAddress);
        $shippingRates = $shippingAddress->getGroupedAllShippingRates();
        foreach ($shippingRates as $carrierRates) {
            foreach ($carrierRates as $rate) {
                $output[] = $this->converter->modelToDataObject($rate, $this->quote->getQuoteCurrencyCode());
            }
        }
        return $output;

    }

    /**
     * Get transform address interface into Array
     *
     * @param \Magento\Framework\Api\ExtensibleDataInterface  $address
     * @return array
     */
    protected function extractAddressData($address)
    {
        $className = \Magento\Customer\Api\Data\AddressInterface::class;
        if ($address instanceof \Magento\Quote\Api\Data\AddressInterface) {
            $className = \Magento\Quote\Api\Data\AddressInterface::class;
        } elseif ($address instanceof \Magento\Quote\Api\Data\EstimateAddressInterface) {
            $className = \Magento\Quote\Api\Data\EstimateAddressInterface::class;
        }
        return $this->getDataObjectProcessor()->buildOutputDataArray(
            $address,
            $className
        );
    }

    /**
     * Gets the data object processor
     *
     * @return \Magento\Framework\Reflection\DataObjectProcessor
     */
    protected function getDataObjectProcessor()
    {
        if ($this->dataProcessor === null) {
            $this->dataProcessor = ObjectManager::getInstance()->get(\Magento\Framework\Reflection\DataObjectProcessor::class);
        }
        return $this->dataProcessor;
    }

}

di.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <preference for="Your\Module\Api\ShipmentForProductEstimationInterface" type="Your\Module\Model\Api\ShipmentForProductEstimation" />
</config>

webapi.xml

<routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Webapi:etc/webapi.xsd">
    <route url="/V1/shipping/estimate/:sku" method="POST">
        <service class="Your\Module\Api\ShipmentForProductEstimationInterface" method="estimateByProductAndAddress"/>
        <resources>
            <resource ref="anonymous" />
        </resources>
    </route>
</routes>

If you are familiar with API endpoints, you will recognise that this can be called via /rest/default/V1/shipping/estimate/:sku, where :sku is the product SKU you would like to check the shipping methods for. The POST object and return JSON will be exactly the same as that of carts/mine/estimate-shipping-methods, so they are interchangeable.

If the rates displayed are different from when you add the item into the active cart, then that suggests the shipping rate fetches the item and/or address data from the session rather than the Magento\Quote\Model\Quote\Address\RateRequest object - if this is the case, then you may need to alter those shipping classes, or contact the developer to have this looked into.

| improve this answer | |
  • This helped me a lot. to debug and i understand. – Padhiyar Gaurang Jul 1 at 9:18
  • if i call /rest/default/V1/shipping/estimate/:sku i got this <response> <message>Specified request cannot be processed.</message> <trace/> </response> – krybbio Jul 8 at 14:50
  • removing default from url works /rest//V1/shipping/estimate/:sku. Magento 2.3.5 – krybbio Jul 8 at 16:11
0

Refer below link for displaying Shipping Rate on Product Details Page

https://webkul.com/blog/how-to-get-estimate-shipping-in-magento2-using-js/

| improve this answer | |
0

The Smithee aproach works, but what about configurable products if one of them has differents size? In frontend doesnt show SKU, how to get SKU from a simpleproduct linked with a configurableproduct?

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.