Magento – Magento 2: how to programatically set free shipping from an observer

free-shippingmagento2quoteshippingshipping-methods

I need to set free shipping in observer, under certain very specific conditions. I can't have free shipping enabled in the back end because customers shouldn't be able to select it.

I already tried these things with no success:

1) set free shipping on address directly in observer. It changes to true but on next call it reverts back to false(0) when checked with getFreeShipping

$address = $quote->getShippingAddress();
$address->setFreeShipping(true);

2) Magento\Quote\Model\Quote\Address:985 requestShippingRates method. Hard-code

$request->setFreeShipping(true);

3) Magento\Quote\Model\Quote\Address\FreeShipping:16 Replace return false with true. This method looks unfinished by the way.

4) Magento\Quote\Model\Quote\Address\Total\Shipping:159. Hard-code:

$address->setFreeShipping(true);

It's equivalent to attempt number 3.

None of these worked. I would appreciate any suggestions. What can I try next? Which class to override?

This is a 'duplicate' of Magento 1 questions:

Best Answer

$address->setFreeShipping(true); 

should work if you apply it before the customer chooses the shipping method. otherwise, you have to play with order level or quote level

quote level

        $totals = 0;
        $quoteItems = $quote->getItemsCollection();
        foreach ($quoteItems as $_quoteItem) {
            $totals += $_quoteItem->getRowTotal();
        }

foreach ($quote->getAllAddresses() as $address) {
                    if ($address->getAddressType() == "shipping") {
                        $address->setShippingMethod('flatrate_flatrate');
                        $address->setCollectShippingRates(true);
                        $address->setSubtotal($totals);
                        $address->setBaseSubtotal($totals);
                        $address->setSubtotalInclTax($totals);
                        $address->setGrandTotal($totals);
                        $address->setShippingAmount(0);
                        $address->setBaseShippingAmount(0);
                        $address->setShippingInclTax(0);
                        $address->setBaseShippingInclTax(0);
                        $address->setBaseGrandTotal($totals);
                        $address->setDiscountAmount(0);
                        $address->setBaseDiscountAmount(0);
                        $address->save();
                    }

                }

or order level

if (($order->getBaseShippingAmount() > 0 ) {
                $order
                    ->setBaseTotalDue($order->getBaseTotalDue() - $order->getBaseShippingAmount())
                    ->setBaseGrandTotal($order->getBaseGrandTotal() - $order->getBaseShippingAmount())
                    ->setGrandTotal($order->getGrandTotal() - $order->getBaseShippingAmount())
                    ->setShippingAmount(0)
                    ->setShippingTaxAmount(0)
                    ->setBaseShippingAmount(0)
                    ->save();
            }