Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] Matrix rates: 'The country isn't available' exception because an incorrect scope is used #391

Open
YachYaroslav opened this issue Aug 8, 2024 · 0 comments

Comments

@YachYaroslav
Copy link

YachYaroslav commented Aug 8, 2024

To Reproduce
Steps to reproduce the behavior:

  1. Prerequisites
    1. Having at least 2 websites (scopes) configured in Magento
    2. Each website has its own list of available countries. (E.g. site1 has only the Netherlands and Belgium, while site2 has only Germany).
    3. One of the websites domain is being used for accessing and using the admin area (e.g. site1).
  2. Click on 'POSTNL' menu item in the admin navigation on the left.
  3. Click on 'Matrix Rates'.
  4. Add a new matrixrate (country: Germany, other values don't matter) for site2 (available countries in the configuration for site2 = [Germany]).
  5. Visit 'Matrix Rates' again to overview the grid with the rates.

Expected result
We see the list with the matrix rates created in our Magento installation.

Actual result
We see the message 'The country isn't available', which is an exception thrown when calling (in Ui/Component/Matrix/Listing/Columns/Country.php):

$countryInfo       = $this->countryInformationAcquirer->getCountryInfo($value);

Workaround
Enable the country for site1, because in getCountryInfo the website configuration of current admin store view (so website site1 and not site2) is used.

Or (rewrite the country name retrieval a bit differently). My own class example (<column name="website_id" class="BytesGuru\PostNL\Ui\Component\Matrix\Listing\Columns\Website"/>):

<?php
declare(strict_types=1);

namespace BytesGuru\PostNL\Ui\Component\Matrix\Listing\Columns;

use Magento\Backend\Model\Auth\Session as AdminSession;
use Magento\Directory\Model\AllowedCountries;
use Magento\Directory\Model\Country as DirectoryCountry;
use Magento\Directory\Model\ResourceModel\Country\Collection as CountryCollection;
use Magento\Directory\Model\ResourceModel\Country\CollectionFactory as CountryCollectionFactory;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\View\Element\UiComponent\ContextInterface;
use Magento\Framework\View\Element\UiComponentFactory;
use Magento\Store\Model\ScopeInterface;
use Magento\Ui\Component\Listing\Columns\Column;

class Country extends Column
{
    private CountryCollectionFactory $countryCollectionFactory;

    /**
     * @var array<int, CountryCollection<DirectoryCountry>>
     */
    private array $countryCollectionPerWebsite;

    private AllowedCountries $allowedCountries;

    private AdminSession $adminSession;

    /**
     * @param ContextInterface $context
     * @param UiComponentFactory $uiComponentFactory
     * @param CountryCollectionFactory $countryCollectionFactory
     * @param AllowedCountries $allowedCountries
     * @param AdminSession $adminSession
     * @param array<string, mixed> $components
     * @param array<string, mixed> $data
     */
    public function __construct(
        ContextInterface         $context,
        UiComponentFactory       $uiComponentFactory,
        CountryCollectionFactory $countryCollectionFactory,
        AllowedCountries         $allowedCountries,
        AdminSession             $adminSession,
        array $components = [],
        array $data       = []
    ) {
        parent::__construct($context, $uiComponentFactory, $components, $data);
        $this->countryCollectionFactory = $countryCollectionFactory;
        $this->allowedCountries         = $allowedCountries;
        $this->adminSession             = $adminSession;
    }

    /**
     * Prepare Data Source
     *
     * @see Website::prepareDataSource()
     *
     * @param array<string, mixed> $dataSource
     *
     * @return array<string, mixed>
     */
    public function prepareDataSource(array $dataSource): array
    {
        if (isset($dataSource['data']['items'])) {
            $fieldName = $this->getData('name');

            foreach ($dataSource['data']['items'] as &$item) {
                $itemList        = explode(',', $item[$fieldName]);
                $countryNameList = [];

                foreach ($itemList as $value) {
                    try {
                        // _website_id is set in \BytesGuru\PostNL\Ui\Component\Matrix\Listing\Columns\Website
                        $countryNameList[] = $this->getFullCountryName($value, $item['_website_id']);
                    } catch (NoSuchEntityException $e) {
                        $countryNameList[] = __(
                            "Country by code '%1' is not enabled for the website '%2'.",
                            $value,
                            $item['_website_id']
                        );
                    }
                }

                $item[$fieldName] = implode(', ', $countryNameList);
            }
        }

        return $dataSource;
    }

    /**
     * Get the full name of a country by its ID.
     *
     * @param string $countryId The ID of the country.
     * @param int|string $websiteId The ID of the website.
     *
     * @return string The full name of the country (based on the current admin user locale).
     *
     * @throws NoSuchEntityException If the country is not available.
     */
    private function getFullCountryName(string $countryId, $websiteId): string
    {
        $websiteId = (int) $websiteId;

        if (isset($this->countryCollectionPerWebsite[$websiteId])) {
            $countryCollection = $this->countryCollectionPerWebsite[$websiteId];
        } else {
            $this->countryCollectionPerWebsite[$websiteId] = $this->countryCollectionFactory->create();
            $countryCollection                             = $this->countryCollectionPerWebsite[$websiteId];
            $allowedCountries                              = $this->allowedCountries->getAllowedCountries(
                ScopeInterface::SCOPE_WEBSITE,
                (string) $websiteId
            );
            if ($allowedCountries) {
                $countryCollection->addFieldToFilter('country_id', ['in' => $allowedCountries])->load();
            }
        }

        /** @var \Magento\Directory\Model\Country|null $country */
        $country = $countryCollection->getItemById($countryId);
        if (!$country) {
            throw new NoSuchEntityException(__("The country isn't available."));
        }

        $currentUser        = $this->adminSession->getUser();
        $currentAdminLocale = $currentUser ? $currentUser->getInterfaceLocale() : 'en_US';

        return $country->getName($currentAdminLocale);
    }
}

Note: we don't have the website_id available on the moment of \TIG\PostNL\Ui\Component\Matrix\Listing\Columns\Country::prepareDataSource execution, because it has been overwritten by its name for each row in \TIG\PostNL\Ui\Component\Matrix\Listing\Columns\Website::prepareDataSource. So the altering is required there as well or another method should be used for getting the full country name for each row.

Screenshots
matrix-rates-exception

  • Magento version: 2.4.3-p3
  • PHP version: 7.4.28
  • TIG PostNL version: 1.15.2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

1 participant