2

I have three stores (store A, B and C). All have different shipping destinations.

Under the website scope, I have set for Store A Japan for the Allow Countries. And for Store B are non-eu countries (US, Canada etc) and for store C EU countries (UK, Germany etc).

However, at the checkout, all the allowed countries in the Default Config is listed.

How can I only show the allowed country in the website scope for the country option?

3 Answers 3

1

The problem lies in the fact that in checkout available countries list is taken from Magento\Store\Model\StoreResolver. In the constructor of this class there is $scopeCode parameters that by default is null. This class has getCurrentStoreId() method which in theory should return id of the current store. But when you look at the code the way it determines current store is based on the $scopeCode and $runMode provided in the constructor. This seems reasonable but the problem is that this class is created in magento via dependency injection at the verly early stage and null is passe as $scopeCode. This makes the getCurrentStoreId() always load default store as the current one. To fix this I've created my custom module with the following code:

in etc/di.xml I've added my own preference for StoreResolverInterface

<preference for="Magento\Store\Api\StoreResolverInterface" type="Custom\MageFix\Store\Model\StoreResolver"/>

Then in class Custom\MageFix\Store\Model\StoreResolver

 namespace Custom\MageFix\Store\Model;

 use Magento\Store\Model\StoreResolver as MagentoStoreResolver;

 class StoreResolver extends MagentoStoreResolver
 {
     /**
      * @return string
      */
     public function getRunMode()
     {
         return $this->runMode;
     }

     /**
      * @param $scopeCode
      * @return $this
      */
     public function setScopeCode($scopeCode)
     {
         $this->scopeCode = $scopeCode;
         return $this;
     }
 }

Finally I've created a plugin to set the current scope code in etc/di.xml

<type name="Magento\Framework\App\FrontControllerInterface">
    <plugin name="set_correct_store_for_store_resolver" type="Custom\MageFix\Framework\App\FrontController" disabled="false" sortOrder="1" />

which looks like this

namespace Custom\MageFix\Framework\App;

use Magento\Framework\App\{
    FrontControllerInterface,
    Request\Http
};
use Magento\Store\{
    Api\StoreResolverInterface, Model\ScopeInterface, Model\StoreManagerInterface
};

class FrontController
{
    /**
     * @var StoreManagerInterface
     */
    protected $storeManager;
    /**
     * @var StoreResolverInterface
     */
    protected $storeResolver;

    /**
     * FrontController constructor.
     * @param StoreManagerInterface $storeManager
     * @param StoreResolverInterface $storeResolver
     */
    public function __construct(
        StoreManagerInterface $storeManager,
        StoreResolverInterface $storeResolver
    ) {

        $this->storeManager = $storeManager;
        $this->storeResolver = $storeResolver;
    }


    /**
     * Set current scope code in store resolver interface object
     *
     * @param FrontControllerInterface $frontController
     * @param Http $request
     */
    public function beforeDispatch(FrontControllerInterface $frontController, Http $request)
    {
        $scopeCode = null;
        switch ($this->storeResolver->getRunMode()) {
            case ScopeInterface::SCOPE_WEBSITE:
                $scopeCode = $this->storeManager->getWebsite()->getCode();
                break;
            case ScopeInterface::SCOPE_STORE:
                $scopeCode = $this->storeManager->getStore()->getCode();
                break;
        }
        if ($scopeCode) {
            $this->storeResolver->setScopeCode($scopeCode);
        }
    }
}
</type>
1
  • Hello, I'm also facing this issue, and tried your solution, but I'm still getting default settings instead the store one, but When I try to log scopeCode in above file then I'm getting store code not the main store code, what should I do? please reply. Apr 5, 2018 at 12:49
1

I had the same issue for magento 2.2 but it was already fixed in that version. So, please check the settings of customer configuration. Change settings to as following.

Store->Configuration->Customers->Customer Configuration->Account Sharing Options in to 'Per Website'.
0

I had the same issue. The problem is related with StoreResolverInterface. Method $this->storeResolver->getCurrentStoreId() always return default store for me. That's why on the checkout are wrong select's options. I have fixed it by override DirectoryDataProcessor block and use StoreManagerInterface instead storeResolver. Unfortunately this class has only private methods, so you have to override all.

In your MODULE/NAME/etc/di.xml

<preference for="Magento\Checkout\Block\Checkout\DirectoryDataProcessor" type="MODULE\NAME\Block\Checkout\DirectoryDataProcessor" />

Then in MODULE/NAME/Block/Checkout create a file called DirectoryDataProcessor.php

<?php
/**
 * Copyright © 2013-2017 Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace MODULE\NAME\Block\Checkout;

use Magento\Directory\Helper\Data as DirectoryHelper;
use Magento\Store\Api\StoreResolverInterface;
use Magento\Store\Model\StoreManagerInterface;

/**
 * Directory data processor.
 *
 * This class adds various country and region dictionaries to checkout page.
 * This data can be used by other UI components during checkout flow.
 */
class DirectoryDataProcessor extends \Magento\Checkout\Block\Checkout\DirectoryDataProcessor
{
    /**
     * @var array
     */
    private $countryOptions;

    /**
     * @var array
     */
    private $regionOptions;

    /**
     * @var \Magento\Directory\Model\ResourceModel\Region\CollectionFactory
     */
    private $regionCollectionFactory;

    /**
     * @var \Magento\Directory\Model\ResourceModel\Region\CollectionFactory
     */
    private $countryCollectionFactory;

    /**
     * @var StoreResolverInterface
     */
    private $storeResolver;

    /**
     * @var DirectoryHelper
     */
    private $directoryHelper;

    /**
     * @var StoreManagerInterface
     */
    private $storeManager;

    /**
     * @param \Magento\Directory\Model\ResourceModel\Country\CollectionFactory $countryCollection
     * @param \Magento\Directory\Model\ResourceModel\Region\CollectionFactory $regionCollection
     * @param StoreResolverInterface $storeResolver
     * @param DirectoryHelper $directoryHelper
     * @param StoreManagerInterface $storeManager
     */
    public function __construct(
        \Magento\Directory\Model\ResourceModel\Country\CollectionFactory $countryCollection,
        \Magento\Directory\Model\ResourceModel\Region\CollectionFactory $regionCollection,
        StoreResolverInterface $storeResolver,
        DirectoryHelper $directoryHelper,
        StoreManagerInterface $storeManager
    ) {
        parent::__construct($countryCollection, $regionCollection, $storeResolver, $directoryHelper);
        $this->countryCollectionFactory = $countryCollection;
        $this->regionCollectionFactory = $regionCollection;
        $this->storeResolver = $storeResolver;
        $this->directoryHelper = $directoryHelper;
        $this->storeManager = $storeManager;
    }

    /**
     * Process js Layout of block
     *
     * @param array $jsLayout
     * @return array
     */
    public function process($jsLayout)
    {
        if (!isset($jsLayout['components']['checkoutProvider']['dictionaries'])) {
            $jsLayout['components']['checkoutProvider']['dictionaries'] = [
                'country_id' => $this->getCountryOptions(),
                'region_id' => $this->getRegionOptions(),
            ];
        }
        return $jsLayout;
    }

    /**
     * Get country options list.
     *
     * @return array
     */
    private function getCountryOptions()
    {
        if (!isset($this->countryOptions)) {
            $this->countryOptions = $this->countryCollectionFactory->create()->loadByStore(
                $this->storeManager->getStore()->getId()
            )->toOptionArray();
            $this->countryOptions = $this->orderCountryOptions($this->countryOptions);
        }

        return $this->countryOptions;
    }

    /**
     * Get region options list.
     *
     * @return array
     */
    private function getRegionOptions()
    {
        if (!isset($this->regionOptions)) {
            $this->regionOptions = $this->regionCollectionFactory->create()->addAllowedCountriesFilter(
                $this->storeManager->getStore()->getId()
            )->toOptionArray();
        }

        return $this->regionOptions;
    }

    /**
     * Sort country options by top country codes.
     *
     * @param array $countryOptions
     * @return array
     */
    private function orderCountryOptions(array $countryOptions)
    {
        $topCountryCodes = $this->directoryHelper->getTopCountryCodes();
        if (empty($topCountryCodes)) {
            return $countryOptions;
        }

        $headOptions = [];
        $tailOptions = [[
            'value' => 'delimiter',
            'label' => '──────────',
            'disabled' => true,
        ]];
        foreach ($countryOptions as $countryOption) {
            if (empty($countryOption['value']) || in_array($countryOption['value'], $topCountryCodes)) {
                array_push($headOptions, $countryOption);
            } else {
                array_push($tailOptions, $countryOption);
            }
        }
        return array_merge($headOptions, $tailOptions);
    }
}

Clear cache and checkout should work fine :) Tested at Magento 2.1.10

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.