Magento – Magento 2 hide shipping methods based on product attribute & shipping address out of USA

hidemagento2shippingshipping-methods

I Would like to hide shipping methods and display a custom message on the checkout page & cart page based on product attribute flag & if the shipping address is outside of the USA.

I used the below plugin but it triggers only logged-in users unable to verify with guest users.

Could you please advise what exact plugin or method want to use?

Note: It should work guest users & registered customers

app/code/Vendor/ShippingRestriction/etc/di.xml

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/ObjectManager/etc/config.xsd">

    <type name="Magento\Quote\Model\ShippingMethodManagement">
        <plugin name="disableshippingmethod" type="Vendor\ShippingRestriction\Plugin\ShippingMethodManagementPlugin"/>
    </type>
</config>

app/code/Vendor/ShippingRestriction/Plugin/ShippingMethodManagementPlugin.php

<?php
namespace Vendor\ShippingRestriction\Plugin;
use Psr\Log\LoggerInterface;
use Exception;
use Magento\Quote\Model\QuoteFactory;
class ShippingMethodManagementPlugin
{
    /**
     * @var LoggerInterface
     */
    private $logger;

    /**
     * @var QuoteFactory
     */
    protected $quoteFactory;


    /**
     * ShippingMethodManagementPlugin constructor.
     * @param LoggerInterface $logger
     * @param \Magento\Customer\Api\AddressRepositoryInterface $addressRepository
     * @param QuoteFactory $quoteFactory
     */
    public function __construct(
    LoggerInterface $logger,
    \Magento\Customer\Api\AddressRepositoryInterface $addressRepository,
    \Magento\Quote\Model\QuoteFactory $quoteFactory
    )
    {
        $this->addressRepository = $addressRepository;
        $this->logger = $logger;
        $this ->quoteFactory = $quoteFactory;
    }

    /**
     * @param \Magento\Quote\Model\ShippingMethodManagement $subject
     * @param $result
     * @param $cartId
     * @param $addressId
     * @return mixed
     */
    public function afterEstimateByAddressId(\Magento\Quote\Model\ShippingMethodManagement $subject, $result, $cartId, $addressId)
    {
        $quote = $this->quoteFactory->create()->load($cartId);
        $items = $quote->getAllVisibleItems();
        $productAttributeValues = array();
        $shippWithinUsaFlag = ['1'];
        foreach ($items as $item) {
            $productAttributeValues[$item->getSku()] = $item->getProduct()->getData('ship_only_within_usa');
        }

        //Fetch current Country Id US/IN based on shipping address id
        $countryIdFlag = $this->getCustomerAddress($addressId);
        //Unset shipping methods based on product attribute & shipping address
        $restrictionFlag = (count(array_intersect($productAttributeValues, $shippWithinUsaFlag))) ? '1' : '0';
        if($countryIdFlag !== "US" && $restrictionFlag === '1'){
            /** @var TYPE_NAME $result */
            foreach ($result as $key => $shippingMethod) {
                 unset($result[$key]);
            }

        }
        return $result;
    }

    /**
     * @param $addressId
     * @return null|string
     */
    public function getCustomerAddress($addressId)
    {
        $countryId = null;
        try {
            $addressRepository = $this->addressRepository->getById($addressId);
            $countryId=$addressRepository->getCountryId();
            return $countryId;
        }  catch (Exception $exception) {
            $this->logger->error("Something went wrong with customer address".$exception->getMessage());
        }
        return $countryId;
    }

}

Best Answer

After did some research on this task I have found a solution for this. Here I am posting the solution based on the requirement it may help others if the will get the same kind of task as per the requirement.

  1. app/code/Vendor/ShippingRestriction/etc/catalog_attributes.xml
 <?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Catalog:etc/catalog_attributes.xsd">
    <group name="quote_item">
        <attribute name="ship_only_within_usa"/>
    </group>
</config>
  1. app/code/Vendor/ShippingRestriction/etc/di.xml
<?xml version="1.0"?>
    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/ObjectManager/etc/config.xsd">
       <preference for="Magento\Shipping\Model\Shipping" type="Vendor\ShippingRestriction\Model\Shipping" />
 </config>
  1. app/code/Vendor/ShippingRestriction/Model/Shipping.php
<?php

namespace Vendor\ShippingRestriction\Model;

class Shipping extends \Magento\Shipping\Model\Shipping {

    /**
     * @param string $carrierCode
     * @param \Magento\Quote\Model\Quote\Address\RateRequest $request
     * @return $this|\Magento\Shipping\Model\Shipping
     */
    public function collectCarrierRates($carrierCode, $request)
    {
        /* @var $carrier \Magento\Shipping\Model\Carrier\AbstractCarrier */
        $carrier = $this->_carrierFactory->createIfActive($carrierCode, $request->getStoreId());
        if (!$carrier) {
            return $this;
        }
        /**-------- Custom logic starts here --------**/

        $restrictionFlag=0;
        $productAttributeValues = array();
        $shippWithInUsaFlag = ['1'];
        $request->getAllItems();
        foreach ($request->getAllItems() as $item) {
            $productAttributeValues[$item->getSku()]=$item->getProduct()->getData('ship_only_within_usa');
        }
        $restrictionFlag = (count(array_intersect($productAttributeValues, $shippWithInUsaFlag))) ? 'restrict' : 'notrestrict';
        $countryCode=$request->getDestCountryId();
        if('restrict' === $restrictionFlag && "US" !== $countryCode) {
            return $this;
        }

         /**-------- Custom logic ends here --------**/

        $carrier->setActiveFlag($this->_availabilityConfigField);
        $result = $carrier->checkAvailableShipCountries($request);
        if (false !== $result && !$result instanceof \Magento\Quote\Model\Quote\Address\RateResult\Error) {
            $result = $carrier->processAdditionalValidation($request);
        }
        /*
         * Result will be false if the admin set not to show the shipping module
         * if the delivery country is not within specific countries
         */
        if (false !== $result) {
            if (!$result instanceof \Magento\Quote\Model\Quote\Address\RateResult\Error) {
                if ($carrier->getConfigData('shipment_requesttype')) {
                    $packages = $this->composePackagesForCarrier($carrier, $request);
                    if (!empty($packages)) {
                        $sumResults = [];
                        foreach ($packages as $weight => $packageCount) {
                            $request->setPackageWeight($weight);
                            $result = $carrier->collectRates($request);
                            if (!$result) {
                                return $this;
                            } else {
                                $result->updateRatePrice($packageCount);
                            }
                            $sumResults[] = $result;
                        }
                        if (!empty($sumResults) && count($sumResults) > 1) {
                            $result = [];
                            foreach ($sumResults as $res) {
                                if (empty($result)) {
                                    $result = $res;
                                    continue;
                                }
                                foreach ($res->getAllRates() as $method) {
                                    foreach ($result->getAllRates() as $resultMethod) {
                                        if ($method->getMethod() == $resultMethod->getMethod()) {
                                            $resultMethod->setPrice($method->getPrice() + $resultMethod->getPrice());
                                            continue;
                                        }
                                    }
                                }
                            }
                        }
                    } else {
                        $result = $carrier->collectRates($request);
                    }
                } else {
                    $result = $carrier->collectRates($request);
                }
                if (!$result) {
                    return $this;
                }
            }
            if ($carrier->getConfigData('showmethod') == 0 && $result->getError()) {
                return $this;
            }
            // sort rates by price
            if (method_exists($result, 'sortRatesByPrice') && is_callable([$result, 'sortRatesByPrice'])) {
                $result->sortRatesByPrice();
            }
            $this->getResult()->append($result);
        }
        return $this;
    }
}