2

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;
    }

}
1

2 Answers 2

1

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;
    }
}
0

If anyone wants to do using plugin then give a try below.

Change required in: Country name and product attribute name and attribute getter :)

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

<type name="Magento\Shipping\Model\Shipping">
        <plugin name="shiprules_collect_valid_methods"
                type="Vendor\ShippingRestriction\Plugin\CollectValidMethods"
                sortOrder="10"
                disabled="false"/>
</type>

File: app/code/Vendor/ShippingRestriction/Plugin/CollectValidMethods.php

<?php

namespace Vendor\ShippingRestriction\Plugin;

use Magento\Catalog\Model\ResourceModel\ProductFactory as ProductResourceModel;
use Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory as RateErrorFactory;

class CollectValidMethods
{
    private $request = false;

    public function __construct(
        ProductResourceModel $productResource,
        RateErrorFactory     $rateErrorFactory
    )
    {
        $this->productResource = $productResource;
        $this->rateErrorFactory = $rateErrorFactory;
    }

    /**
     * @param \Magento\Shipping\Model\Shipping $subject
     * @param \Magento\Quote\Model\Quote\Address\RateRequest $request
     */
    public function beforeCollectRates(
        \Magento\Shipping\Model\Shipping               $subject,
        \Magento\Quote\Model\Quote\Address\RateRequest $request
    )
    {
        $this->request = $request;
    }

    /**
     * @param \Magento\Shipping\Model\Shipping $subject
     * @param $result
     * @return \Magento\Shipping\Model\Shipping|void
     */
    public function afterCollectRates(
        \Magento\Shipping\Model\Shipping $subject,
                                         $result)
    {
        /*$writer = new \Zend\Log\Writer\Stream(BP . '/var/log/checkoutAttrRestrictions.log');
        $logger = new \Zend\Log\Logger();
        $logger->addWriter($writer);*/

        $countryCode = $this->request->getDestCountryId();
        //This can be easily replace with some config helper
        $countryCodeToCheck = 'US';
        $attrName = 'ship_only_within_usa';

        if ($countryCodeToCheck !== $countryCode) {
            return $subject;
        }
        //$logger->info($countryCode);
        $attributeRestrict = false;
        $restrictionFlag = 0;
        $productAttributeValues = array();
        $shippWithInUsaFlag = ['1'];
        $allItems = $this->request->getAllItems();
        foreach ($allItems as $item) {
            $_product = $item->getProduct();
            //In case straightforward attribute
            //$attrVal = $_product->getResource()->getData($attrName);
            //In case attribute has frontend value
            $attrVal = $_product->getResource()->getAttribute($attrName)->getFrontend()->getValue($_product);

            if (!empty($attrVal) && in_array($attrVal,$shippWithInUsaFlag)) {
                $attributeRestrict = true;
                break;
            }
        }
        $result = $subject->getResult();
        if (!($rates = $result->getAllRates())) {
            return $subject;
        }

        //$logger->info($attributeRestrict);
        if(!$attributeRestrict){
            return $subject;
        }
        $result->reset();
        $errorMsg = 'Sorry, no quotes are available for this order at this time';
        foreach ($rates as $rate) {
            $restrict = false;
            if ($countryCode == $countryCodeToCheck ) {
                $restrict = true;
            }
            $error = $this->rateErrorFactory->create();
            $error->setCarrier($rate->getCarrier());
            $error->setCarrierTitle($rate->getCarrierTitle());
            $error->setErrorMessage($errorMsg);
            $result->append($error);

            if (!$restrict) {
                $result->append($rate);
            }
        }
        return $subject;
    }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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