magento2 icon indicating copy to clipboard operation
magento2 copied to clipboard

Magento/OfflineShipping/Model/Carrier/Tablerate.php::collectRates is modifying the request for subsequent carriers

Open ioweb-gr opened this issue 1 year ago • 8 comments

Preconditions and environment

  • 2.4.1 -> 2.4.6-p3

This is a rather technical issue I stumbled upon that I think needs fixing as it is prone to cause bugs.

Steps to reproduce

This is a general issue with the carriers but I happened to stumble on this one while trying to impelment free shipping for a new carrier. The fact that the request wasn't having the correct packageValue on my method worried me so I tried to see the reason behind it. Finally I understood.

Because all variables which refer to objects are passed by reference any setter will actually modify the \Magento\Quote\Model\Quote\Address\RateRequest object for the next carrier invocation.

This is an issue coming from function \Magento\Shipping\Model\Shipping::collectRates

At line

https://github.com/magento/magento2/blob/bfea78834c11594d6d802f4dde0188485fb77274/app/code/Magento/Shipping/Model/Shipping.php#L243C1-L245C14

where as you can see the same request is reused in the foreach loop for all carriers.

Thus each carrier is actually reusing request information that was previously modfied from other carriers' collectRates.

I believe this to be a mistake because if information exists in the \Magento\Quote\Model\Quote\Address\RateRequest that the carrier needs to calculate it's rates, if any other method modifies it, it could affect the results of the upcoming carrier collectRates result.

For my case the

            $request->setPackageValue($newPackageValue);
            $request->setPackageValueWithDiscount($newPackageValue);

Caused weird values to come in the PackageValueWithDisocunt instead of 156 (my order total) I was getting 30.19 due to the disabled method of TableRates

The quick solution would be to pass the object by value instead of by reference.

Is there a reason it's passed by reference instead of by value? I don't know. If any core developer can inform us why it would be great.

I'm thinking that if I changed the value for the items or the weight or anything that could have an effect on rate calculation by mistake in any subsequent method. So it would have significant impact on other carriers.

Expected result

The \Magento\Quote\Model\Quote\Address\RateRequest is provided to each carrier in it's original data

Actual result

The \Magento\Quote\Model\Quote\Address\RateRequest is provided to each carrier modified by each carrier.

Additional information

Let me give a tangible example.

Create a carrier which will internally unset the Free Shipping e.g. $request->setFreeShipping(false).

Then subsequent carriers which rely on $request->getFreeShipping() to set free shipping, will actually fail to set it because the previous carrier removed the information from them.

It happened to me with Amasty's TableRates module which unset the Free Shipping from the Request.

Adding the following patch to module-shipping fixed for me

Index: Model/Shipping.php
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/Model/Shipping.php b/Model/Shipping.php
--- a/Model/Shipping.php
+++ b/Model/Shipping.php	(date 1704388901162)
@@ -241,7 +241,7 @@
             );

             foreach ($carriers as $carrierCode => $carrierConfig) {
-                $this->collectCarrierRates($carrierCode, $request);
+                $this->collectCarrierRates($carrierCode, clone $request);
             }
         } else {
             if (!is_array($limitCarrier)) {
@@ -256,7 +256,7 @@
                 if (!$carrierConfig) {
                     continue;
                 }
-                $this->collectCarrierRates($carrierCode, $request);
+                $this->collectCarrierRates($carrierCode, clone $request);
             }
         }


Release note

No response

Triage and priority

  • [ ] Severity: S0 - Affects critical data or functionality and leaves users without workaround.
  • [ ] Severity: S1 - Affects critical data or functionality and forces users to employ a workaround.
  • [ ] Severity: S2 - Affects non-critical data or functionality and forces users to employ a workaround.
  • [ ] Severity: S3 - Affects non-critical data or functionality and does not force users to employ a workaround.
  • [ ] Severity: S4 - Affects aesthetics, professional look and feel, “quality” or “usability”.

ioweb-gr avatar Jan 04 '24 16:01 ioweb-gr

Hi @ioweb-gr. 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:


Join Magento Community Engineering Slack and ask your questions in #github channel. :warning: According to the Magento Contribution requirements, all issues must go through the Community Contributions Triage process. Community Contributions Triage is a public meeting. :clock10: You can find the schedule on the Magento Community Calendar page. :telephone_receiver: The triage of issues happens in the queue order. If you want to speed up the delivery of your contribution, join the Community Contributions Triage session to discuss the appropriate ticket.

m2-assistant[bot] avatar Jan 04 '24 16:01 m2-assistant[bot]

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:

  • [ ] 1. Verify that issue has all the required information. (Preconditions, Steps to reproduce, Expected result, Actual result).
  • [ ] 2. Verify that issue has a meaningful description and provides enough information to reproduce the issue.
  • [ ] 3. Add Area: XXXXX label to the ticket, indicating the functional areas it may be related to.
  • [ ] 4. Verify that the issue is reproducible on 2.4-develop branch
    Details- Add the comment @magento give me 2.4-develop instance to deploy test instance on Magento infrastructure.
    - If the issue is reproducible on 2.4-develop branch, please, add the label Reproduced on 2.4.x.
    - If the issue is not reproducible, add your comment that issue is not reproducible and close the issue and stop verification process here!
  • [ ] 5. Add label Issue: Confirmed once verification is complete.
  • [ ] 6. Make sure that automatic system confirms that report has been added to the backlog.

m2-assistant[bot] avatar Jan 05 '24 14:01 m2-assistant[bot]

Hello @ioweb-gr,

Thank you for the report and collaboration!

Verified this issue on 2.4-develop, but in our case it was not reproducible.

Created two carriers namely custom shipping and custom free shipping. In custom shipping carrier, made freeShipping true, and changed the packageValue, but in the custom free shipping carrier's rateRequest we didn't see any of these changes. In our case one carrier did not alter the value in other carrier's rate request.

Please find the attached module and let us know if we are missing anything. Shipping carrier modules.zip

Thank you.

engcom-November avatar Jan 08 '24 10:01 engcom-November

The modules were loaded in the wrong order so you didn't see the effect.

FreeShipVendor.zip

Try this modified versrion where both carriers after calculation of their amount set free shipping to true for the subsequent invocation.

You'll notice in checkout either one or the other (depending on the internal array pointer) will actually have free shipping offered while they shouldn't.

image

Sorry for the buggy code I didn't get in depth as to why it shows method twice as I only focused on reproducing the free shipping

ioweb-gr avatar Jan 08 '24 10:01 ioweb-gr

Hello @ioweb-gr,

Thank you for the quick response!

We were able to reproduce the issue with the provided module. Please take a look at the screenshot: image freeShipping is set to true in custom free shipping module, but in custom shipping we can see that price is 0, because in here freeShipping has also become true from the other carrier.

Hence confirming the issue.

Thank you.

engcom-November avatar Jan 09 '24 15:01 engcom-November

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

github-jira-sync-bot avatar Jan 09 '24 15:01 github-jira-sync-bot

: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.

m2-assistant[bot] avatar Jan 09 '24 15:01 m2-assistant[bot]

Hi @engcom-November thanks for verifying. I think you should adjust priority on this one. It's a real hidden gem of an issue that has direct impact on the shipping values. I'm pretty sure it has great potential to break a mechants' checkout as it was for us until we found it out. I hope the PR then could be processed a bit faster.

ioweb-gr avatar Jan 09 '24 16:01 ioweb-gr