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

Shipping method radios become disabled when checkout page refreshed #7497

Closed wsagen closed 7 years ago

wsagen commented 7 years ago

Preconditions

  1. Magento CE v2.1 installed
  2. Tested with UPS carrier enabled using UPS XML endpoint and Flat rate enabled

Steps to reproduce

  1. Add product to the cart
  2. Proceed to checkout as a guest
  3. Enter valid address details
  4. Wait for shipping rates to be displayed
  5. Refresh page (Ctrl + R for example)

Expected result

  1. Page reloads showing shipping rates with active radio buttons

Actual result

  1. Page reloads with shipping rates but radio buttons are disabled
checkout

We've also observed this on initial checkout page load but not able to provide instructions to reliably reproduce this every time.

wsagen commented 7 years ago

This is affecting several live clients at present. Work around is to modify the address fields which refreshes the shipping rates and radio buttons become selectable again.

hpatelgit commented 7 years ago

Yes i am also getting same issue when add new address at checkout and then on refresh page radios become disabled.

Even i have upgrade magento with latest version.

Thanks

SerhiyShkolyarenko commented 7 years ago

Reproduced. Created internal issue MAGETWO-61384. Thank you!

Tapin1 commented 7 years ago

Any fix for this issue?

SerhiyShkolyarenko commented 7 years ago

@Tapin1 issue is in queue for fixing. We don't have estimates yet.

rsisco commented 7 years ago

We too are experiencing this issue on Magento 2.1.2 and can reproduce the problem with the following steps:

  1. Sign into an existing (or create a new) customer account with an address in the address book
  2. Add one or more items to the shopping cart
  3. Click Checkout in the mini cart
  4. Click the New Address button on the shipping page
  5. Complete the new address form and submit

The radio buttons will then all disable after a few seconds although the radio button labels remain clickable.

hpatelgit commented 7 years ago

I have upgrade magento with latest 2.1.3 and still this issue is present.

Frits1980 commented 7 years ago

Is their anyone who knows which file is setting the disabled attribute to the html input field? I've searched but I can't find the file. Because I really need to work around this issue for the time being.

reijervano commented 7 years ago

Has anyone a fix for this?

Frits1980 commented 7 years ago

Actually the problem disappeared when I updated all my extensions.

reijervano commented 7 years ago

This problem is still there in the latest version!

ddeppner commented 7 years ago

I'm seeing this in 2.1.3. It's a pretty serious bug as it impacts checkout and for a customer that isn't aware of a workaround (basically EVERYBODY), if they get the bug, they cannot complete their checkout. Triggering this bug is hard. It happens sometimes and not other times. I've seen it happen after adding addresses, or when going back to the shipping screen to change sometime after previously advancing to the payment screen. Since it only happens some of the time, it's hard to calculate the impact, but based on a few days grappling with trying to debug and trigger this, it probably happens 5% or 10% of the time... And having 5%+ of customers lose their ability to advance through checkout is a major blocker to our ability to launch our new M2 site.

This bug impacts customer flow through checkout and is pretty serious. I hope this is going to be escalated and fixed VERY quickly. This is not a minor UI inconvenience to be fixed at some later date. This is a critical and inconsistent bug in the checkout...

If anyone has more information on what is actually causing this, or a workaround so I can hack something in there, I'd love more information. So far I haven't found anything more substantial than the above bug report.

joshuaswarren commented 7 years ago

FYI, the original bug report mentions this is impacting Magento CE 2.1. I can confirm this is impacting multiple Magento EE (Enterprise) 2.1.3 sites as well.

reijervano commented 7 years ago

The listener in Magento/Checkout/view/frontend/web/js/model/shipping-rate-service.js is called multiple times, because the quote.shippingAddress is changed (most of the fields are empty the second time, very strange). A workaround could by to block the execution of the function when some mandatory fields are not present of empty.

wsagen commented 7 years ago

Any update on this? I've also noted that during page refresh the shipping-rate-service.js anonymous function is called twice. Once it passes into setShippingRates.js setShippingRates, the radios become disabled after line shippingRates(ratesData); The ratesData contains the exact shipping rates both times this function is called, the second time the radios are disabled.

ddeppner commented 7 years ago

Some observations:

The core issue here isn't that shipping methods become disabled, but that they don't become re-enabled. I don't have time to dig deeply into the architecture here or root causes. But I do need a quick fix so I can launch my M2 site and not wait for months until this bug fix gets fixed, with no feedback on an ETA.

So... I've tried to figure out an effective workaround in order to get my site launched without this critical bug that prevents checkout sometimes.

The code that disables the radio buttons is in the frontend in Magento_Ui/js/block-loader.js. That file is also the location of the code that is supposed to re-enable them. Check out the addBlockLoader() and removeBlockLoader() functions. addBlockLoader() disables any input or select elements in the hierarchy under the element that triggers the blockLoader, which in this case is the <li> with id of "opc-shipping_method" that contains all the methods and their radio buttons. The block loader is triggered via the data-bind attribute, specifically "blockLoader: isLoading". You can confirm this by just removing that so the block loader isn't triggered and the bug goes away (although you also don't get the spinning icon to indicate to the user that data is loading, so that's not a good fix).

When the block loader is invoked, it removes focus from any currently selected input field, makes them all disabled, and then sticks the loading icon over the top of the block being loaded. This is actually redundant, in a sense. You don't need to disable the input elements since they are now unselectable due to another element being on top of them.

I didn't mess around enough to understand why the removeBlockLoader() function can't seem to correctly remove the disabled property from input elements in some cases but not others. This may be due to the fact that the addBlockLoader() and removeBlockLoader() code is getting called twice some of the time, and it wasn't designed to be re-entrant. I.e., if you call addBlockLoader, then call it a second time, then call removeBlockLoader and then call it a second time, there definitely would be a logical problem with these functions that would trigger the bug. I didn't test enough to determine if that's what's actually happening or not. The mechanism there would be that on the first call to addBlockLoader, it's adding a "_disabled" class to save the state of any input elements that started out disabled, so later the removeBlockLoader call will only un-disable the elements that were not previously disabled. If you call addBlockLoader twice before a call to removeBlockLoader, the second addBlockLoader call would see that everything is disabled, mark everything with a "_disabled" class, and then none of them would be enabled after that by subsequent calls to removeBlockLoader. This is just speculation on one possible cause of the bug, though. I didn't check into it other than reading the code and confirming it should not be called twice. There may be a situation where it ends up being called twice in this manner (but only some of the time) due to variation in the timing of when various AJAX requests return. Note that since both of these functions work on all input elements under a parent object, this also means that the block loader should never be used on multiple blocks where one is the parent of another, or a bug like this could also be triggered. This could possibly impact other forms on other parts of the site? Again, just a theory. I don't know. Maybe there's something else going on.

Given that there is actually no point to disabling the elements because you can't select them while the loading icon is displayed anyway... The simple workaround is to comment out this line in block-loader.js:

//element.find('input, select').prop('disabled', true);

Since I'm not sure if that might have other unintended consequences on other pages, I opted to just wrap that in a conditional to prevent it from executing on this particular element in the checkout. So I think this is an effective workaround until this bug is fixed correctly... In addBlockLoader(), test for the id of the element being passed in, and if it's 'opc-shipping_method', skip disabling the radio buttons.

    if (element.attr('id') != 'opc-shipping_method') {
       element.find('input, select').prop('disabled', true);
    }

After implementing this on our staging site, we haven't experienced this bug in our testing.

Hope this helps...

erikhansen commented 7 years ago

We are experiencing this on multiple Magento 2.1.x merchant sites.

Anyone who is interested in seeing a resolution on this issue: you should add a 👍 reaction to the original post in order for it to get more attention from the Magento team.

royvanos commented 7 years ago

@veloraven @SerhiyShkolyarenko any idea when this issue will be fixed? It's creating a lot of unnecessary confusion...

AirmanAJK commented 7 years ago

I just spent quite a bit of time debugging this one. It appears that whenever you have a postcode that will show up at checkout page load, either by a logged in user with a saved address, or a user who used the "estimate shipping" before moving to checkout, the call to update shipping rates is performed TWICE. Once to get the initial rate, but the second one seems to be due to the postcode getting filled out at page load, triggering a knockout binding event 2000 milliseconds after it's updated.

In any case, the radio buttons get disabled at Magento_Checkout/js/model/shipping-service.js once the setShippingRates function calls the line "shippingRates(ratesData)" a second time. It looks like knockout is toggling the disabled status (though I can't find where), because once I step over that line, the radio buttons become disabled and stay that way. I believe it is toggling because the first time it is called the radio buttons go from disabled to enabled, and the second call 2 seconds later does the opposite.

My hack fix is to add the following line at the end of the setShippingRates(rates) function: jQuery(".table-checkout-shipping-method input[type=radio]").prop("disabled", false);

I realize that this is not a good fix, and the true reason they are being disabled should be addressed, but as the commenters in this thead have said, this is a store breaking bug that will cause customers to leave.

I hope this helps someone willing to take over the debugging.

reijervano commented 7 years ago

@vrann Hello Eugene, were you guys able to frix this on the Hackaton?

vherasymenko commented 7 years ago

On latest 2.1-develop version another issue. Radio buttons are disappears after refresh page using (CTRL + R )

Turv commented 7 years ago

Still an issue 5 months on.. do we have a legitimate work around?

ddeppner commented 7 years ago

@Turv My Feb 17 comment above is a successful workaround that we're using on our live site, at least until this is fixed properly.

deepakkn85 commented 7 years ago

I will start looking this issue.

vzabaznov commented 7 years ago

Hi @wsagen, i'm sorry for such delay, bug that @K7Triton described confirmed, https://jira.corp.magento.com/browse/MAGETWO-70465 was created to fix all further problems in complex

vzabaznov commented 7 years ago

Hi @wsagen MAGETWO-70727 issue was fixed and will be delivered in 2.1.8 release, sorry to all for such delay, please let me know if your issue was fixed and feel free to write here or reopen if still have this issue

magento-team commented 7 years ago

Internal ticket to track issue progress: MAGETWO-68875

korostii commented 7 years ago

Hi @vzabaznov, 2.1.8 is not even released yet. Did you by any chance mean "please test whether the issue is reproducible for you when using the source code taken from branch 2.1.8-preview using the clone from repository method of installation" ? Otherwise it's quite misleading,

Please don't close the issue as long as the fix isn't in any of the releases yet. It's utterly confusing. Please consider using the GitHub's "Milestones" feature instead.

nghitrum commented 6 years ago

Still, have this problem in 2.2.5.

nbennett25 commented 6 years ago

Also having the same issue in 2.2.5 - can we get this ticket re-opened?

mariuszjeruzal commented 5 years ago

Problem exists 2.2.6

Nastradoomus commented 5 years ago

Just heard a compaint from a customer who could not proceed to check out due to the fact that shipping methods with a price stays hidden after adding post code. I can't reproduce this error and debug it. Can someone verify the steps that led to the problem?

Free shipping methods are open.

2.2.6

jeffekg commented 5 years ago

Getting this on 2.3.2 EE

Edit: This was caused by the B2B modules. After removing them, issue went away.

emanuelerangan commented 5 years ago

Magento 2.3.2 CE also affected by this problem, here's my workaround:

setShippingRates: function (ratesData) { shippingRates(ratesData); shippingRates.valueHasMutated(); checkoutDataResolver.resolveShippingRates(ratesData);

setTimeout(function() {
    var container = document.getElementById('co-shipping-method-form');
    if (container) {
        var inputs = container.getElementsByTagName('input');

        ratesData.forEach(function(rate) {
            if (rate.available) {
                var name = rate.carrier_code + '_' + rate.method_code;
                for (var i = 0; i < inputs.length; i++) {
                    if (inputs[i].value === name) {
                        inputs[i].removeAttribute('disabled');
                    }
                }
            }
        });
    }
}, 1000);

})

marcosdsdba commented 5 years ago

This problem in 2.3.1 too.

josiahglassone commented 5 years ago

I also have this problem in v2.3.1

cannycookie commented 5 years ago

Still an issue in 2.3.3. @devs to replicate we have to enable bandwidth throttling to simulate slow connections. On the cart If you re-edit the postcode WHILST the subtotals are still calculating then the radios stayed disabled.

daniel-berko-work commented 4 years ago

I can second that the issue remains on 2.3.3 CE.

andresams commented 4 years ago

I was able to successfully reproduce the issue on Magento 2.3.4 and create a real fix for it.

How to reproduce the error

  1. Create a new account without an address
  2. Add a product to the cart
  3. Go to the shopping cart page and make sure the "State" field is NOT selected
  4. Enter a postcode and wait until the shipping rates are loaded
  5. Change the last digit of the postcode to another number and repeat this operation multiple times, quickly enough to trigger the shipping rates calculator multiple times.

After a few times, you will see that the radio fields will not get re-enabled.

What is really happening?

In order to understand what really happens, please, look at this code snippet:

vendor/magento/module-checkout/view/frontend/web/js/model/shipping-rate-processor/new-address.js

            storage.post(
                serviceUrl, payload, false
            ).done(function (result) {
                rateRegistry.set(address.getCacheKey(), result);
                shippingService.setShippingRates(result);
            }).fail(function (response) {
                shippingService.setShippingRates([]);
                errorProcessor.process(response);
            }).always(function () {
                shippingService.isLoading(false);
            });

When we successfully receive the results from the POST request, we are using shippingService.setShippingRates() to send the new data to a knockout observable, which then triggers the updates to the HTML. However, right after this function runs, we also run the "always" callback, which is responsible for removing the block loader and re-enabling the radio fields (they got disabled when we ran shippingService.isLoading(true), which displays the block loader). The problem here is that there is a small delay of a few milliseconds between the time the observable is notified and when the HTML is in fact updated, so we end up running shippingService.isLoading(true) BEFORE the new HTML is rendered, causing a scenario where the radio fields never get re-enabled.

How to fix it

All we have to do is to add a delay of a few milliseconds to this function, to make sure that the new data is rendered when we remove the blocker and try to re-enable the form fields:

vendor/magento/module-ui/view/base/web/js/block-loader.js

function removeBlockLoader(element) {
    setTimeout(function() {
        if (!element.has(blockLoaderClass).length) {
            return;
        }
        element.find(blockLoaderClass).remove();
        element.find('input:not("._disabled"), select:not("._disabled")').prop('disabled', false);
        element.find('input:disabled, select:disabled').removeClass('_disabled');
        element.removeClass(blockContentLoadingClass);
    }, 500);
}

This will fix the issue in all locations where the block loader is used, including the cart and checkout pages.