magento / magento2

Prior to making any Submission(s), you must sign an Adobe Contributor License Agreement, available here at: https://opensource.adobe.com/cla.html. All Submissions you make to Adobe Inc. and its affiliates, assigns and subsidiaries (collectively “Adobe”) are subject to the terms of the Adobe Contributor License Agreement.
http://www.magento.com
Open Software License 3.0
11.55k stars 9.32k forks source link

Upgrading Magento resets general/region/state_required when new country with required state/region is added. #37796

Closed Hexmage closed 5 months ago

Hexmage commented 1 year ago

Summary

When a new Country is added with required states (in 2.4.6 this was Czechia). The general/region/state_required gets reset to the default countries with a required state (or at the very least readds all those countries). Which means that if you had modified this configuration and have some form of custom code that assumes region is disabled. This could break your shop.

Examples

This is the Data patch for adding the Regions of Czechia.

    public function apply()
    {
        /** @var DataInstaller $dataInstaller */
        $dataInstaller = $this->dataInstallerFactory->create();
        $dataInstaller->addCountryRegions(
            $this->moduleDataSetup->getConnection(),
            $this->getDataForCzechia()
        );

        return $this;
    }

And this is the DataInstaller which adds the regions as expected. But then follows this up by resetting the state required countries.

    public function addCountryRegions(AdapterInterface $adapter, array $data)
    {
        /**
         * Fill table directory/country_region
         * Fill table directory/country_region_name for en_US locale
         */
        foreach ($data as $row) {
            $bind = ['country_id' => $row[0], 'code' => $row[1], 'default_name' => $row[2]];
            $adapter->insert($this->resourceConnection->getTableName('directory_country_region'), $bind);
            $regionId = $adapter->lastInsertId($this->resourceConnection->getTableName('directory_country_region'));
            $bind = ['locale' => 'en_US', 'region_id' => $regionId, 'name' => $row[2]];
            $adapter->insert($this->resourceConnection->getTableName('directory_country_region_name'), $bind);
        }
        /**
         * Upgrade core_config_data general/region/state_required field.
         */
        $countries = $this->data->getCountryCollection()->getCountriesWithRequiredStates();
        $adapter->update(
            $this->resourceConnection->getTableName('core_config_data'),
            [
                'value' => implode(',', array_keys($countries))
            ],
            [
                'scope="default"',
                'scope_id=0',
                'path=?' => \Magento\Directory\Helper\Data::XML_PATH_STATES_REQUIRED
            ]
        );
    }

Proposed solution

Only add the modified country to general/region/state_required.

Release note

No response

Triage and priority

m2-assistant[bot] commented 1 year ago

Hi @Hexmage. Thank you for your report. To speed up processing of this issue, make sure that the issue is reproducible on the vanilla Magento instance following Steps to reproduce. To deploy vanilla Magento instance on our environment, Add a comment to the issue:

thedotwriter commented 1 year ago

I'll add that $this->data->getCountryCollection()->getCountriesWithRequiredStates() was only returning the countries for the default website. Therefore, if this default website only allows shipping to a few countries, you won't get a complete list of countries with required states.

Anyway, with the patch below, I've completely removed the need for getCountriesWithRequiredStates() so that's not an issue anymore.

Here's the patch. This prevents losing custom required states each time regions are updated for a country.

--- a/vendor/magento/module-directory/Setup/DataInstaller.php
+++ b/vendor/magento/module-directory/Setup/DataInstaller.php
@@ -16,25 +16,17 @@
 class DataInstaller
 {
     /**
-     * @var \Magento\Directory\Helper\Data
-     */
-    private $data;
-
-    /**
      * @var ResourceConnection
      */
     private $resourceConnection;

     /**
      * DatInstaller constructor.
-     * @param \Magento\Directory\Helper\Data $data
      * @param ResourceConnection $resourceConnection
      */
     public function __construct(
-        \Magento\Directory\Helper\Data $data,
         ResourceConnection $resourceConnection
     ) {
-        $this->data = $data;
         $this->resourceConnection = $resourceConnection;
     }

@@ -57,14 +49,30 @@
             $bind = ['locale' => 'en_US', 'region_id' => $regionId, 'name' => $row[2]];
             $adapter->insert($this->resourceConnection->getTableName('directory_country_region_name'), $bind);
         }
+
+        // Get country ID from regions.
+        $region = reset($data);
+        $countryId = $region[0];
+
+        $select = $adapter->select()
+            ->from($this->resourceConnection->getTableName('core_config_data'), 'value')
+            ->where('path = ?', \Magento\Directory\Helper\Data::XML_PATH_STATES_REQUIRED)
+            ->where('scope = ?', 'default')
+            ->where('scope_id = ?', 0);
+        $currentRequiredStates = $adapter->fetchOne($select);
+
+        $currentRequiredStates = explode(',', $currentRequiredStates);
+        if (!in_array($countryId, $currentRequiredStates)) {
+            $currentRequiredStates[] = $countryId;
+        }
+
         /**
          * Upgrade core_config_data general/region/state_required field.
          */
-        $countries = $this->data->getCountryCollection()->getCountriesWithRequiredStates();
         $adapter->update(
             $this->resourceConnection->getTableName('core_config_data'),
             [
-                'value' => implode(',', array_keys($countries))
+                'value' => implode(',', $currentRequiredStates)
             ],
             [
                 'scope="default"',
m2-assistant[bot] commented 1 year ago

Hi @engcom-November. Thank you for working on this issue. In order to make sure that issue has enough information and ready for development, please read and check the following instruction: :point_down:

engcom-November commented 1 year ago

Hello @Hexmage,

Thank you for the report and collaboration!

We were able to reproduce this issue on 2.4-develop. To check wether general/region/state_required is being reset after adding country with required state, we removed United States from state_required.

image

Then added Japan to state_required by using a custom module. After upgrading magento it can be seen that Japan country code JP has been added to general/region/state_required, but it also did reset the general/region/state_required, by adding United States to the list.

image

Please find below the custom module. RequiredVendor.zip

Hence confirming the issue.

Thank you.

github-jira-sync-bot commented 1 year ago

:white_check_mark: Jira issue https://jira.corp.adobe.com/browse/AC-9630 is successfully created for this GitHub issue.

m2-assistant[bot] commented 1 year ago

:white_check_mark: Confirmed by @engcom-November. Thank you for verifying the issue.
Issue Available: @engcom-November, You will be automatically unassigned. Contributors/Maintainers can claim this issue to continue. To reclaim and continue work, reassign the ticket to yourself.

Yaroslav1301 commented 1 year ago

@magento I am working on this

Zwernemann commented 2 months ago

This is still happening with magento updates. After update to 2.47.-p2 a lot of countries were set to "state is required", that were unchecked before the update. Countries like swizerland and denmark. We got a lot of complaints, because customers couldn't order.