2

Background

We have a custom shipping method "Click & Collect", with this custom shipping method is a custom calculator on the checkout_cart_index page which will return all the nearest stockists to the given postcode.

The Problem

Most of it works fine, but the shipping carrier method and address are not being set and when the user proceeds to checkout they are required to fill this information in again.

The Code

define(
    [
        'ko',
        'jquery',
        'Magento_Ui/js/form/form',
        'uiRegistry',
        'Magento_Checkout/js/model/quote',
        'Magento_Checkout/js/model/resource-url-manager',
        'mage/storage',
        'Magento_Checkout/js/action/create-shipping-address',
        'Magento_Checkout/js/model/error-processor',
        'Magento_Checkout/js/model/shipping-service',
        'Magento_Checkout/js/model/cart/totals-processor/default',
        'Magento_Checkout/js/model/shipping-rate-registry'
    ],
    function (
        ko,
        $,
        Component,
        uiRegistry,
        quote,
        resourceUrlManager,
        storage,
        createShippingAddress,
        errorProcessor,
        shippingService,
        totalsDefaultProvider,
        rateRegistry
    ) {
        'use strict';

        var ClickAndCollectConfig = window.checkout.clickandcollect;

        return Component.extend({
            defaults: {
                template: 'Convert_ClickAndCollect/cart/click-collect-stores'
            },
            formId: "#selectWarehouseForm",
            parent: {},
            postCode: ko.observable(''),
            wareHouses: ko.observableArray(),
            address: {},
            isWarehouseSelected: ko.observable(""),

            /**
             * @override
             */
            initialize: function () {
                this._super();
                $(document).ready(() => this.ready());
                return this;
            },

            ready: function () {
                this.parent = uiRegistry.get(this.parentName);
            },

            selectWarehouse: function(warehouseId) {
                if ($('#s_method_freeshipping').length) {
                    return false;
                }
                if (!this.validateForm(this.formId)) {
                    return false;
                }
                if (warehouseId != null) {
                    let body = $('body').loader();
                    let uri = this.getFormAction();
                    let payload = JSON.stringify({
                        addressInformation: {
                            address: {
                                countryId: 'AU',
                                postcode: this.postCode(),
                                regionId: 0
                            },
                            shipping_carrier_code: ClickAndCollectConfig.shipping.carrier_code,
                            shipping_method_code: warehouseId
                        }
                    });
                    this.address = createShippingAddress(payload);
                    shippingService.isLoading(true);
                    storage.post(
                        uri, payload, true
                    ).done(
                        (resp) => this.selectWarehouseSuccess(resp, this.getInputValue(warehouseId))
                    ).fail(
                        (resp) => this.selectWarehouseFail(resp)
                    ).always(
                        () => this.selectWarehouseAlways()
                    );
                }
                return false;
            },

            selectWarehouseSuccess: function(result, checked) {
                if (result) {
                    rateRegistry.set(this.address.getCacheKey(), result);
                    shippingService.setShippingRates(result);
                    quote.setTotals(result);
                }
                this.isWarehouseSelected(checked);
            },

            selectWarehouseFail: function(response) {
                console.error("There was an error saving shipping the method, no address will be used at this time");
                shippingService.setShippingRates([]);
                errorProcessor.process(response);
            },

            selectWarehouseAlways: function() {
                let body = $('body').loader();
                body.loader('hide');
                shippingService.isLoading(false);
            },

            validateForm: function (form) {
                return $(form).validation() && $(form).validation('isValid');
            },

            getInputValue(warehouseId) {
                return ClickAndCollectConfig.shipping.carrier_code + "_" + warehouseId;
            },

            getFormAction() {
                return resourceUrlManager.getUrlForTotalsEstimationForNewAddress(quote);
            },
        });
    }
);

The main methods of concern here are:

selectWarehouse: This is the main method that gets fired on clicking the radio input. It builds the payload parameters and then performs the POST request which should garner the correct response and set the shipping method and address ready for the next page (checkout).

selectWarehouseSuccess: Where the quote and shipping totals are updated with the response of selectWarehouse.

getInputValue: This is the required format for the radio inputs which goes [Shipping Carrier Code]_[Shipping_Method_Code]

getFormAction: Returns the correct URI endpoint to POST to using the 'Magento_Checkout/js/model/resource-url-manager' which should be either '/guest-carts/:cartId/totals-information' or '/carts/mine/totals-information'.

When building the request, from the network tab of the browsers console, I can see that the correct POST has been sent with my created params and it even returns a valid 200 response. But in selectWarehouseSuccess the result is false and quote.

I am taking my inspiration from Magento_Checkout/js/model/cart/totals-processor/default.js - which does it similarly.

After a couple days of this, I believe it's time for a fresh pair of eyes. Am I doing this correctly? Is there an easier way?

Thanks for reading

0

Okay, I have figured out how to set the shipping method programmatically:

Firstly, all my dependencies:

define(
    [
        'ko',
        'jquery',
        'Magento_Ui/js/form/form',
        'uiRegistry',
        'Magento_Checkout/js/model/quote',
        'Magento_Checkout/js/model/resource-url-manager',
        'Magento_Checkout/js/action/create-shipping-address',
        'Magento_Checkout/js/model/error-processor',
        'Magento_Checkout/js/model/shipping-service',
        'Magento_Checkout/js/model/cart/totals-processor/default',
        'Magento_Checkout/js/model/shipping-rate-registry',
        'Magento_Checkout/js/action/select-shipping-method',
        'Magento_Checkout/js/checkout-data'
    ],
    function (
        ko,
        $,
        Component,
        uiRegistry,
        quote,
        resourceUrlManager,
        createShippingAddress,
        errorProcessor,
        shippingService,
        totalsDefaultProvider,
        rateRegistry,
        selectShippingMethodAction,
        checkoutData
    ) { 

    ...

Then, the magic promise:

                setShippingMethod: function (carrier, method, title) {
                    return new Promise((resolve, reject) => {
                        let data = {
                            carrier_code: carrier,
                            method_code: method,
                            carrier_title: title,
                            method_title: title
                        };
                        selectShippingMethodAction(data);
                        checkoutData.setSelectedShippingRate(code + '_' + method);
                        if (quote.shippingMethod()) {
                            resolve(quote.shippingMethod());
                        } else {
                            reject(quote.shippingMethod());
                        }
                    });
                }

So then in your event (or whatever):

                    setShippingMethod(
                        methodData['carrier_code'], methodData['warehouse_id'], methodData['title']
                    ).then(
                        (shippingMethod) => { 
                             totalsDefaultProvider.estimateTotals(quote.shippingAddress());
                        }
                    ).catch(
                        (err) => failHandlerMethod(err)
                    );

Optional, you can also add the shipping address:

                    let payload = JSON.stringify({
                        addressInformation: {
                            address: {
                                countryId: "country",
                                postcode: "postcode",
                                regionId: "region"
                            },
                            shipping_carrier_code: "carrier_code",
                            shipping_method_code: "method_code"
                        }
                    });
                    let addr = createShippingAddress(payload);
                    rateRegistry.set(address.getCacheKey(), addr);

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.