Magento 2 – change shipping costs during checkout
In this tutorial I show how to change Magento 2 shipping costs during checkout. Sometimes this is needed, if your shipping cost depends on product information, which you can not get working with existing shipping methods or price rules.
Magento 2 – change shipping costs during checkout
First of all, you need to know the basics about shipping and Magento 2. There is a difference to Magento 1, which costs me hours to find out. If you check Magento Core in you vendor directory, there you will find 2! shipping modules:
- module-shipping
- module-offline-shipping
I tried to overload a module-shipping class to adjust shipping costs in checkout but it didn’t worked. For me, shipping sounds as a good place for this till I realized, that tablerates is an offline shipping method on there is actually an offline shipping method too.
Change shipping rates
Based on a specific product option I want to offer free shipping…not really a free shipping method, but normal tablerates with shipping cost 0. For this we need to overload Tablerate Carrier method collectRates, which is done the following way:
Create a di.xml file in you etc folder of your Magento 2 module with the following code:
1 2 3 | <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/ObjectManager/etc/config.xsd"> <preference for="Magento\OfflineShipping\Model\Carrier\Tablerate" type="MyCompany\MyModule\Model\Carrier\Tablerate" /> </config> |
You need to adjust MyCompany\MyModule to your module name. This di.xml handles your rewrites which are defined by preference tag. You are actually doing an overloading of a class which means: you tell Magento to use your model instead of core model. If your model extends core model and only rewrites needed functions, it is a normal overloading. If you do not extend core model, you need to copy whole file, which is a rewrite. Please do not make a rewrite, because otherwise patches and core updates are ignored!
Your MyCompany\MyModule\Model\Carrier\Tablerate look like this:
1 2 3 4 5 6 7 8 9 10 | <?php namespace MyCompany\MyModule\Model\Carrier; use Magento\Quote\Model\Quote\Address\RateRequest; class Tablerate extends \Magento\OfflineShipping\Model\Carrier\Tablerate { public function collectRates(RateRequest $request) { ... |
Function collectRates is a 1:1 copy of core function. You can do your shipping cost update before this line of code:
1 | $method->setPrice($shippingPrice); |
You only need to set a different $shippingPrice than is actually set.
Example
The following example code demonstrates how to change shipping cost based on product option. For this we created all products with options “Pickup” and “Shipping” (and some more options like “Assembly”). You have to choose one of them before you add this product to cart. If one product in cart has “Pickup” as option, shipping costs are always 0. Because of different methods, rates and constellation of products, this simple code sets all shipping costs to 0, if only one product hast “Pickup” as option.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); $items = $request->getAllItems(); $free = false; //loop all cart items and check options foreach($items as $item) { $itemOptions = $item->getOptions(); $itemOptions = unserialize($itemOptions[0]['value']); //get set option ids for parent and child option foreach ($itemOptions['options'] as $key=>$value) { $parentOptionId = $key; $optionId = $value; $option = $objectManager->get('\Magento\Catalog\Model\Product\Option')->load($parentOptionId); $product_id = $option->getProductId(); $product = $objectManager->get('\Magento\Catalog\Model\Product')->load($product_id); $options = $objectManager->get('\Magento\Catalog\Model\Product\Option')->getProductOptionCollection($product); //loop all parent options foreach($options as $option) { $values = $option->getValues(); //loop all child options foreach($values as $value) { //if name is Pickup and it is set for this cart item -> free shipping if($value->getTitle() == "Pickup" && $value->getOptionTypeId() == $optionId) { $free = true; break; } } } } } if($free) $shippingPrice = 0.0; |
This example code also shows how to get product options during checkout. Maybe you can use this otherwise else.
Conclusion
It is quite easy to change shipping costs during checkout in Magento 2 if you overload the correct model. I hope this article is helpful for you if your are working on a similar problem.
Thank you ! You save my life
Thank you for this comment. Good to read, that my work helped you.
Thanks
but i think we should use observer such as mg1
for two week i searched for event observer but i does not find it
i try your code
I create a custom shipping module. My custom shipping module is working fine. it is showing the price that I filled in the admin on cart or checkout page.
Now I need to change a shipping price based on the pin code that user fill or choose in available shipping in magento 2.
Can you please help me how can I do this.?
hey, the above example should do the trick, you need to extend \Magento\OfflineShipping\Model\Carrier\Tablerate and set shippingPrice to that value, your module gets from adminhtml (database)
I can’t use the Tablerate shipping method because I need customer’s pin code to get the difference between the product’s available pin code and customer’s pin code.
I need to run a API to find a shipping rate based on the pin code.
Sorry, no idea how to do it with API.
Shipping Price is changing but not adding in GrandTotal.
if this happens, you need to re-collect totals for your quote object like:
I am getting this error after overwrite model tablerate while i have ran setup upgrade command and remove var generation folder
Fatal error: Uncaught TypeError: Argument 5 passed to Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate::__construct() must be an instance of Magento\OfflineShipping\Model\Carrier\Tablerate, instance of Iksula\Checkoutcustomization\Model\Carrier\Tablerates given,
try to recompile or remove di folder from var. Does it work now?
not working on magento 2.2