menu

SiteGenesis / Server-side JS / Source: app_storefront_controllers/cartridge/controllers/COShipping.js

'use strict';

/**
 * Controller for the default single shipping scenario.
 * Single shipping allows only one shipment, shipping address, and shipping method per order.
 *
 * @module controllers/COShipping
 */

/* API Includes */
var CustomerMgr = require('dw/customer/CustomerMgr');
var HashMap = require('dw/util/HashMap');
var Resource = require('dw/web/Resource');
var ShippingMgr = require('dw/order/ShippingMgr');
var Site = require('dw/system/Site');
var Transaction = require('dw/system/Transaction');
var URLUtils = require('dw/web/URLUtils');

/* Script Modules */
var app = require('~/cartridge/scripts/app');
var guard = require('~/cartridge/scripts/guard');

/**
 * Prepares shipments. Theis function separates gift certificate line items from product
 * line items. It creates one shipment per gift certificate purchase
 * and removes empty shipments. If in-store pickup is enabled, it combines the
 * items for in-store pickup and removes them.
 * This function can be called by any checkout step to prepare shipments.
 *
 * @transactional
 * @return {Boolean} true if shipments are successfully prepared, false if they are not.
 */
function prepareShipments() {
    var cart, homeDeliveries;
    cart = app.getModel('Cart').get();

    homeDeliveries = Transaction.wrap(function () {
        var homeDeliveries = false;

        cart.updateGiftCertificateShipments();
        cart.removeEmptyShipments();

        if (Site.getCurrent().getCustomPreferenceValue('enableStorePickUp')) {
            homeDeliveries = cart.consolidateInStoreShipments();

            session.forms.singleshipping.inStoreShipments.shipments.clearFormElement();
            app.getForm('singleshipping.inStoreShipments.shipments').copyFrom(cart.getShipments());
        } else {
            homeDeliveries = true;
        }

        return homeDeliveries;
    });

    return homeDeliveries;
}

/**
 * Starting point for the single shipping scenario. Prepares a shipment by removing gift certificate and in-store pickup line items from the shipment.
 * Redirects to multishipping scenario if more than one physical shipment is required and redirects to billing if all line items do not require
 * shipping.
 *
 * @transactional
 */
function start() {
    var cart = app.getModel('Cart').get();
    var physicalShipments, pageMeta, homeDeliveries;

    if (!cart) {
        app.getController('Cart').Show();
        return;
    }
    // Redirects to multishipping scenario if more than one physical shipment is contained in the basket.
    physicalShipments = cart.getPhysicalShipments();
    if (Site.getCurrent().getCustomPreferenceValue('enableMultiShipping') && physicalShipments && physicalShipments.size() > 1) {
        app.getController('COShippingMultiple').Start();
        return;
    }

    // Initializes the singleshipping form and prepopulates it with the shipping address of the default
    // shipment if the address exists, otherwise it preselects the default shipping method in the form.
    if (cart.getDefaultShipment().getShippingAddress()) {
        app.getForm('singleshipping.shippingAddress.addressFields').copyFrom(cart.getDefaultShipment().getShippingAddress());
        app.getForm('singleshipping.shippingAddress.addressFields.states').copyFrom(cart.getDefaultShipment().getShippingAddress());
        app.getForm('singleshipping.shippingAddress').copyFrom(cart.getDefaultShipment());
    } else {
        if (customer.authenticated && customer.registered && customer.addressBook.preferredAddress) {
            app.getForm('singleshipping.shippingAddress.addressFields').copyFrom(customer.addressBook.preferredAddress);
            app.getForm('singleshipping.shippingAddress.addressFields.states').copyFrom(customer.addressBook.preferredAddress);
        }
    }
    session.forms.singleshipping.shippingAddress.shippingMethodID.value = cart.getDefaultShipment().getShippingMethodID();

    // Prepares shipments.
    homeDeliveries = prepareShipments();

    Transaction.wrap(function () {
        cart.calculate();
    });

    // Go to billing step, if we have no product line items, but only gift certificates in the basket, shipping is not required.
    if (cart.getProductLineItems().size() === 0) {
        app.getController('COBilling').Start();
    } else {
        pageMeta = require('~/cartridge/scripts/meta');
        pageMeta.update({
            pageTitle: Resource.msg('singleshipping.meta.pagetitle', 'checkout', 'SiteGenesis Checkout')
        });
        app.getView({
            ContinueURL: URLUtils.https('COShipping-SingleShipping'),
            Basket: cart.object,
            HomeDeliveries: homeDeliveries
        }).render('checkout/shipping/singleshipping');
    }
}

/**
 * Handles the selected shipping address and shipping method. Copies the
 * address details and gift options to the basket's default shipment. Sets the
 * selected shipping method to the default shipment.
 *
 * @transactional
 * @param {module:models/CartModel~CartModel} cart - A CartModel wrapping the current Basket.
 */
function handleShippingSettings(cart) {
    Transaction.wrap(function () {
        var defaultShipment, shippingAddress;
        defaultShipment = cart.getDefaultShipment();
        shippingAddress = cart.createShipmentShippingAddress(defaultShipment.getID());

        shippingAddress.setFirstName(session.forms.singleshipping.shippingAddress.addressFields.firstName.value);
        shippingAddress.setLastName(session.forms.singleshipping.shippingAddress.addressFields.lastName.value);
        shippingAddress.setAddress1(session.forms.singleshipping.shippingAddress.addressFields.address1.value);
        shippingAddress.setAddress2(session.forms.singleshipping.shippingAddress.addressFields.address2.value);
        shippingAddress.setCity(session.forms.singleshipping.shippingAddress.addressFields.city.value);
        shippingAddress.setPostalCode(session.forms.singleshipping.shippingAddress.addressFields.postal.value);
        shippingAddress.setStateCode(session.forms.singleshipping.shippingAddress.addressFields.states.state.value);
        shippingAddress.setCountryCode(session.forms.singleshipping.shippingAddress.addressFields.country.value);
        shippingAddress.setPhone(session.forms.singleshipping.shippingAddress.addressFields.phone.value);
        defaultShipment.setGift(session.forms.singleshipping.shippingAddress.isGift.value);
        defaultShipment.setGiftMessage(session.forms.singleshipping.shippingAddress.giftMessage.value);

        cart.updateShipmentShippingMethod(cart.getDefaultShipment().getID(), session.forms.singleshipping.shippingAddress.shippingMethodID.value, null, null);
        cart.calculate();

        cart.validateForCheckout();
    });
}

/**
 * Updates shipping address for the current customer with information from the singleshipping form. If a cart exists, redirects to the
 * {@link module:controllers/COShipping~start|start} function. If one does not exist, calls the {@link module:controllers/Cart~Show|Cart controller Show function}.
 *
 * @transactional
 */
function updateAddressDetails() {
    var addressID, segments, lookupCustomer, lookupID, address, profile;
    //Gets an empty cart object from the CartModel.
    var cart = app.getModel('Cart').get();

    if (!cart) {
        app.getController('Cart').Show();
        return;
    }
    addressID = request.httpParameterMap.addressID.value ? request.httpParameterMap.addressID.value : request.httpParameterMap.dwfrm_singleshipping_addressList.value;
    segments = addressID.split('??');

    lookupCustomer = customer;
    lookupID = addressID;

    if (segments.length > 1) {
        profile = CustomerMgr.queryProfile('email = {0}', segments[0]);
        lookupCustomer = profile.getCustomer();
        lookupID = segments[1];
    }

    address = lookupCustomer.getAddressBook().getAddress(lookupID);
    app.getForm('singleshipping.shippingAddress.addressFields').copyFrom(address);
    app.getForm('singleshipping.shippingAddress.addressFields.states').copyFrom(address);

    Transaction.wrap(function () {
        var defaultShipment = cart.getDefaultShipment();
        var shippingAddress = cart.createShipmentShippingAddress(defaultShipment.getID());

        shippingAddress.setFirstName(session.forms.singleshipping.shippingAddress.addressFields.firstName.value);
        shippingAddress.setLastName(session.forms.singleshipping.shippingAddress.addressFields.lastName.value);
        shippingAddress.setAddress1(session.forms.singleshipping.shippingAddress.addressFields.address1.value);
        shippingAddress.setAddress2(session.forms.singleshipping.shippingAddress.addressFields.address2.value);
        shippingAddress.setCity(session.forms.singleshipping.shippingAddress.addressFields.city.value);
        shippingAddress.setPostalCode(session.forms.singleshipping.shippingAddress.addressFields.postal.value);
        shippingAddress.setStateCode(session.forms.singleshipping.shippingAddress.addressFields.states.state.value);
        shippingAddress.setCountryCode(session.forms.singleshipping.shippingAddress.addressFields.country.value);
        shippingAddress.setPhone(session.forms.singleshipping.shippingAddress.addressFields.phone.value);
        defaultShipment.setGift(session.forms.singleshipping.shippingAddress.isGift.value);
        defaultShipment.setGiftMessage(session.forms.singleshipping.shippingAddress.giftMessage.value);
    });

    start();
}

/**
 * Form handler for the singleshipping form. Handles the following actions:
 * - __save__ - saves the shipping address from the form to the customer address book. If in-store
 * shipments are enabled, saves information from the form about in-store shipments to the order shipment.
 * Flags the save action as done and calls the {@link module:controllers/Cart~Show|Cart controller Show function}.
 * If it is not able to save the information, calls the {@link module:controllers/Cart~Show|Cart controller Show function}.
 * - __selectAddress__ - updates the address details and page metadata, sets the ContinueURL property to COShipping-SingleShipping,  and renders the singleshipping template.
 * - __shipToMultiple__ - calls the {@link module:controllers/COShippingMultiple~Start|COShippingMutliple controller Start function}.
 * - __error__ - calls the {@link module:controllers/COShipping~Start|COShipping controller Start function}.
 */
function singleShipping() {
    var singleShippingForm = app.getForm('singleshipping');
    singleShippingForm.handleAction({
        save: function () {
            var cart = app.getModel('Cart').get();
            if (!cart) {
                // @FIXME redirect
                app.getController('Cart').Show();
                return;
            }

            handleShippingSettings(cart);

            // Attempts to save the used shipping address in the customer address book.
            if (customer.authenticated && session.forms.singleshipping.shippingAddress.addToAddressBook.value) {
                app.getModel('Profile').get(customer.profile).addAddressToAddressBook(cart.getDefaultShipment().getShippingAddress());
            }
            // Binds the store message from the user to the shipment.
            if (Site.getCurrent().getCustomPreferenceValue('enableStorePickUp')) {
                if (!app.getForm(session.forms.singleshipping.inStoreShipments.shipments).copyTo(cart.getShipments())) {
                    require('./Cart').Show();
                    return;
                }
            }

            // Mark step as fulfilled.
            session.forms.singleshipping.fulfilled.value = true;
            // @FIXME redirect
            app.getController('COBilling').Start();
        },
        selectAddress: function () {
            updateAddressDetails(app.getModel('Cart').get());

            var pageMeta = require('~/cartridge/scripts/meta');
            pageMeta.update({
                pageTitle: Resource.msg('singleshipping.meta.pagetitle', 'checkout', 'SiteGenesis Checkout')
            });
            app.getView({
                ContinueURL: URLUtils.https('COShipping-SingleShipping')
            }).render('checkout/shipping/singleshipping');
        },
        shipToMultiple: app.getController('COShippingMultiple').Start,
        error: function () {
            response.redirect(URLUtils.https('COShipping-Start'));
        }
    });
}

/**
 * Selects a shipping method for the default shipment. Creates a transient address object, sets the shipping
 * method, and returns the result as JSON response.
 *
 * @transaction
 */
function selectShippingMethod() {
    var cart = app.getModel('Cart').get();
    var TransientAddress = app.getModel('TransientAddress');
    var address, applicableShippingMethods;

    if (!cart) {
        app.getView.render('checkout/shipping/selectshippingmethodjson');
        return;
    }
    address = new TransientAddress();
    address.countryCode = request.httpParameterMap.countryCode.stringValue;
    address.stateCode = request.httpParameterMap.stateCode.stringValue;
    address.postalCode = request.httpParameterMap.postalCode.stringValue;
    address.city = request.httpParameterMap.city.stringValue;
    address.address1 = request.httpParameterMap.address1.stringValue;
    address.address2 = request.httpParameterMap.address2.stringValue;

    applicableShippingMethods = cart.getApplicableShippingMethods(address);

    Transaction.wrap(function () {
        cart.updateShipmentShippingMethod(cart.getDefaultShipment().getID(), request.httpParameterMap.shippingMethodID.stringValue, null, applicableShippingMethods);
        cart.calculate();
    });

    app.getView({
        Basket: cart.object
    }).render('checkout/shipping/selectshippingmethodjson');
}

/**
 * Determines the list of applicable shipping methods for the default shipment of
 * the current basket. The applicable shipping methods are based on the
 * merchandise in the cart and any address parameters included in the request.
 * Changes the shipping method of this shipment if the current method
 * is no longer applicable. Precalculates the shipping cost for each applicable
 * shipping method by simulating the shipping selection i.e. explicitly adds each
 * shipping method and then calculates the cart.
 * The simulation is done so that shipping cost along
 * with discounts and promotions can be shown to the user before making a
 * selection.
 * @transaction
 */
function updateShippingMethodList() {
    var i, address, applicableShippingMethods, shippingCosts, currentShippingMethod, method;
    var cart = app.getModel('Cart').get();
    var TransientAddress = app.getModel('TransientAddress');

    if (!cart) {
        app.getController('Cart').Show();
        return;
    }
    address = new TransientAddress();
    address.countryCode = request.httpParameterMap.countryCode.stringValue;
    address.stateCode = request.httpParameterMap.stateCode.stringValue;
    address.postalCode = request.httpParameterMap.postalCode.stringValue;
    address.city = request.httpParameterMap.city.stringValue;
    address.address1 = request.httpParameterMap.address1.stringValue;
    address.address2 = request.httpParameterMap.address2.stringValue;

    applicableShippingMethods = cart.getApplicableShippingMethods(address);
    shippingCosts = new HashMap();
    currentShippingMethod = cart.getDefaultShipment().getShippingMethod() || ShippingMgr.getDefaultShippingMethod();

    // Transaction controls are for fine tuning the performance of the data base interactions when calculating shipping methods
    Transaction.begin();

    for (i = 0; i < applicableShippingMethods.length; i++) {
        method = applicableShippingMethods[i];

        cart.updateShipmentShippingMethod(cart.getDefaultShipment().getID(), method.getID(), method, applicableShippingMethods);
        cart.calculate();
        shippingCosts.put(method.getID(), cart.preCalculateShipping(method));
    }

    Transaction.rollback();

    Transaction.wrap(function () {
        cart.updateShipmentShippingMethod(cart.getDefaultShipment().getID(), currentShippingMethod.getID(), currentShippingMethod, applicableShippingMethods);
        cart.calculate();
    });

    session.forms.singleshipping.shippingAddress.shippingMethodID.value = cart.getDefaultShipment().getShippingMethodID();

    app.getView({
        Basket: cart.object,
        ApplicableShippingMethods: applicableShippingMethods,
        ShippingCosts: shippingCosts
    }).render('checkout/shipping/shippingmethods');
}

/**
 * Determines the list of applicable shipping methods for the default shipment of
 * the current customer's basket and returns the response as a JSON array. The
 * applicable shipping methods are based on the merchandise in the cart and any
 * address parameters are included in the request parameters.
 */
function getApplicableShippingMethodsJSON() {
    var cart = app.getModel('Cart').get();
    var TransientAddress = app.getModel('TransientAddress');
    var address, applicableShippingMethods;

    address = new TransientAddress();
    address.countryCode = request.httpParameterMap.countryCode.stringValue;
    address.stateCode = request.httpParameterMap.stateCode.stringValue;
    address.postalCode = request.httpParameterMap.postalCode.stringValue;
    address.city = request.httpParameterMap.city.stringValue;
    address.address1 = request.httpParameterMap.address1.stringValue;
    address.address2 = request.httpParameterMap.address2.stringValue;

    applicableShippingMethods = cart.getApplicableShippingMethods(address);

    app.getView({
        ApplicableShippingMethods: applicableShippingMethods
    }).render('checkout/shipping/shippingmethodsjson');
}

/**
 * Renders a form dialog to edit an address. The dialog is opened
 * by an Ajax request and ends in templates, which trigger a JavaScript
 * event. The calling page of this dialog is responsible for handling these
 * events.
 */
function editAddress() {
    session.forms.shippingaddress.clearFormElement();
    var shippingAddress = customer.getAddressBook().getAddress(request.httpParameterMap.addressID.stringValue);

    if (shippingAddress) {
        app.getForm(session.forms.shippingaddress).copyFrom(shippingAddress);
        app.getForm(session.forms.shippingaddress.states).copyFrom(shippingAddress);
    }

    app.getView({
        ContinueURL: URLUtils.https('COShipping-EditShippingAddress')
    }).render('checkout/shipping/shippingaddressdetails');
}

/**
 * Form handler for the shippingAddressForm. Handles the following actions:
 *  - __apply__ - if form information cannot be copied to the platform, it sets the ContinueURL property to COShipping-EditShippingAddress and
 *  renders the shippingAddressDetails template. Otherwise, it renders the dialogapply template.
 *  - __remove__ - removes the address from the current customer's address book and renders the dialogdelete template.
 */
function editShippingAddress() {
    var shippingAddressForm = app.getForm('shippingaddress');
    shippingAddressForm.handleAction({
        apply: function () {
            var object = {};
            // @FIXME what is this statement used for?
            if (!app.getForm(session.forms.shippingaddress).copyTo(object) || !app.getForm(session.forms.shippingaddress.states).copyTo(object)) {
                app.getView({
                    ContinueURL: URLUtils.https('COShipping-EditShippingAddress')
                }).render('checkout/shipping/shippingaddressdetails');
            } else {
                app.getView().render('components/dialog/dialogapply');
            }
        },
        remove: function () {
            customer.getAddressBook().removeAddress(session.forms.shippingaddress.object);
            app.getView().render('components/dialog/dialogdelete');
        }
    });
}

/*
* Module exports
*/

/*
* Web exposed methods
*/
/** Starting point for the single shipping scenario.
 * @see module:controllers/COShipping~start */
exports.Start = guard.ensure(['https'], start);
/** Selects a shipping method for the default shipment.
 * @see module:controllers/COShipping~selectShippingMethod */
exports.SelectShippingMethod = guard.ensure(['https', 'get'], selectShippingMethod);
/** Determines the list of applicable shipping methods for the default shipment of the current basket.
 * @see module:controllers/COShipping~updateShippingMethodList */
exports.UpdateShippingMethodList = guard.ensure(['https', 'get'], updateShippingMethodList);
/** Determines the list of applicable shipping methods for the default shipment of the current customer's basket and returns the response as a JSON array.
 * @see module:controllers/COShipping~getApplicableShippingMethodsJSON */
exports.GetApplicableShippingMethodsJSON = guard.ensure(['https', 'get'], getApplicableShippingMethodsJSON);
/** Renders a form dialog to edit an address.
 * @see module:controllers/COShipping~editAddress */
exports.EditAddress = guard.ensure(['https', 'get'], editAddress);
/** Updates shipping address for the current customer with information from the singleshipping form.
 * @see module:controllers/COShipping~updateAddressDetails */
exports.UpdateAddressDetails = guard.ensure(['https', 'get'], updateAddressDetails);
/** Form handler for the singleshipping form.
 * @see module:controllers/COShipping~singleShipping */
exports.SingleShipping = guard.ensure(['https', 'post', 'csrf'], singleShipping);
/** Form handler for the shippingAddressForm.
 * @see module:controllers/COShipping~editShippingAddress */
exports.EditShippingAddress = guard.ensure(['https', 'post'], editShippingAddress);

/*
 * Local methods
 */
exports.PrepareShipments = prepareShipments;

X Privacy Update: We use cookies to make interactions with our websites and services easy and meaningful, to better understand how they are used. By continuing to use this site you are giving us your consent to do this. Privacy Policy.