menu

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

'use strict';

/**
 * Controller for in-store functionality that is mostly used on the Product Detail Page.
 * __Note:__core functions are used when the UI cartridge is not in the cartridge path.
 *
 * @module controllers/StoreInventory
 * @TODO Simplify the logic, there is no need to return the stores as JSON to render them in the client
 */

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

/**
 * Sets the store for a given line item.
 */
function setStore() {
    var parameterMap = request.httpParameterMap;

    var Cart = app.getModel('Cart');
    var cart = Cart.get();

    var result = setStoreInLineItem(cart.object, parameterMap.plid.stringValue, parameterMap.storeid.stringValue,
        parameterMap.storepickup.stringValue);

    let r = require('~/cartridge/scripts/util/Response');

    if (result.ErrorCode) {
        r.renderJSON({
            success: false,
            ErrorCode: result.ErrorCode
        });
        return;
    }

    cart.removeEmptyShipments();

    if (empty(session.custom.storeId) && !empty(parameterMap.storeid.value)) {
        session.custom.storeId = parameterMap.storeid.value;
    }

    r.renderJSON({
        success: true
    });
}

/**
 * Identifies the store selected for in-store pickup and renders selected store
 * (storelocator/storepickup/coreshowselectedstore template). If the
 * httpParameter map does not have a storeId parameter, calls the
 * {@link module:controllers/StoreInventory~showSetStore|showSetStore} function.
 * @TODO - Is this outdated?
 */
function showSelectedStoreCore() {
    session.custom.storeId = request.httpParameterMap.storeId.value;

    if ((session.custom.zipcode !== null) && (session.custom.storeId !== null)) {
        app.getView().render('storelocator/storepickup/coreshowselectedstore');
        return;
    }

    showSetStore();
}

/**
 * Renders the page that displays the set of stores where a product item is in stock
 * (storelocator/storepickup/coresetstore template).
 * If the httpParameter map does not have a storeId parameter, calls the
 * {@link module:controllers/StoreInventory~showZipCode|showZipCode} function.
 */
function showSetStore(stores, storeAvailabilityMap) {
    if (session.custom.zipcode !== null) {
        // TODO Stores and storeAvailabilityMap expected?
        app.getView({
            Stores: stores,
            storeAvailabilityMap: storeAvailabilityMap
        }).render('storelocator/storepickup/coresetstore');
        return;
    }

    showZipCode();
}

/**
 * Renders the zip codes if the richUI cartridge is not in the path.
 */
function showZipCode() {
    app.getView().render('storelocator/storepickup/corezipcode');
}

/**
 * Creates a HashMap with store information for all stores where a
 * product is available for in-store pickup.
 */
function showAvailableStores() {
    session.custom.zipcode = request.httpParameterMap.zipCode.value;

    var stores = lookupByZipCode().Stores;

    var Product = app.getModel('Product');
    var product = Product.get(request.httpParameterMap.pid.stringValue),
        storeAvailabilityMap;
    if (!product) {
        storeAvailabilityMap = new dw.util.HashMap();
    } else {
        storeAvailabilityMap = getStoreAvailabilityMap(stores, product.object);
    }

    showSetStore(stores, storeAvailabilityMap);
}

/**
 * Calls the {@link module:controllers/StoreInventory~setZipCodeCore|setZipCodeCore} to set the
 * zip code. If the session does not have a custom.zipcode property, calls the
 * {@link module:controllers/StoreInventory~showAvailableStores|showAvailableStores} function.
 */
function cartSetZipCodeCore() {
    if (session.custom.zipcode !== null) {
        showAvailableStores();
    } else {
        setZipCodeCore();
    }
}

/**
 * Calls the {@link module:controllers/StoreInventory~showZipCode|showZipCode} function.
 */
function setZipCodeCore() {
    showZipCode();
}
/**
 * Uses the httpParameter map plid, storeid, and storepickup parameters to
 * set the store and its inventory list for the product line item. If this results in
 * an error, calls the {@link module:controllers/Cart~show|Cart Controller Show function}.
 * If not, then it removes any empty shipments, adds the storeid to the session custom properties
 * and calls the {@link module:controllers/Cart~show|Cart Controller Show function}.
 */
function setStoreCore() {
    var CartController = app.getController('Cart');
    var parameterMap = request.httpParameterMap;

    var Cart = app.getModel('Cart');
    var cart = Cart.get();

    var result = setStoreInLineItem(cart.object, parameterMap.plid.stringValue, parameterMap.storeid.stringValue,
        parameterMap.storepickup.stringValue);

    if (result.ErrorCode) {
        // @FIXME Pass the error to the cart controller
        CartController.Show();
        return;
    }

    cart.removeEmptyShipments();

    if (empty(session.custom.storeId) && !empty(parameterMap.storeid.value)) {
        session.custom.storeId = parameterMap.storeid.value;
    }

    CartController.Show();
}

/**
 * Gets a list of stores with the given product, or product line item's
 * availability to sell (requires rich_UI cartridge). If the storeID is not set it
 * will be set within the session object. Renders a JSON object.
 *
 * Sample URL to call ....Stores-Inventory?pid=2239622&zipCode=01803
 */
function inventory() {
    var storesList = lookupByZipCode().Stores;

    var Product = app.getModel('Product');
    var lineitem = getProductLineItem(request.httpParameterMap.pid.stringValue);

    var product;
    if (!lineitem) {
        product = Product.get(request.httpParameterMap.pid.stringValue).object;
    } else {
        product  = lineitem.product;
    }

    var storeAvailabilityMap = getStoreAvailabilityMap(storesList, product, lineitem);

    // Creates JSON representation.
    var stores = [];

    for (var i = 0, len = storesList.length; i < len; i++) {
        var store = storesList[i];

        // TODO This may need to be changed to ("inventoryListId" in store.custom) ?
        // store.custom.inventoryListId : "";
        var inventoryListId = store.custom.inventoryListId || '';
        var inventoryList = dw.catalog.ProductInventoryMgr.getInventoryList(inventoryListId);
        var inventoryRec = inventoryList ? inventoryList.getRecord(Product.ID) : null;

        if (i > 9) {
            break;
        }

        if (storeAvailabilityMap.get(store.ID) === null) {
            continue;
        }

        stores.push({
            storeId: store.ID,
            status: storeAvailabilityMap.get(store.ID),
            statusclass: storeAvailabilityMap.get(store.ID) === dw.web.Resource.msg('cart.store.availableinstore', 'checkout', null) ? 'store-in-stock' : 'store-error',
            quantity: inventoryRec ? inventoryRec.ATS.value : 0,
            address1: store.address1,
            city: store.city,
            stateCode: store.stateCode,
            postalCode: store.postalCode
        });
    }

    let r = require('~/cartridge/scripts/util/Response');
    r.renderJSON(stores);
}

/**
 * Sets the preferred store for the session.
 */
function setPreferredStore() {
    session.custom.storeId = request.httpParameterMap.storeId.value;

    getPreferredStore();
}

/**
 * Gets the preferred store from the session.
 */
function getPreferredStore() {
    let r = require('~/cartridge/scripts/util/Response');
    r.renderJSON([{
        storeId: session.custom.storeId
    }]);
}

/**
 * Sets the users zip code for at the session.
 */
function setZipCode() {
    session.custom.zipcode = request.httpParameterMap.zipCode.value;

    getZipCode();
}

/**
 * Gets the users zip code from the session.
 */
function getZipCode() {
    let r = require('~/cartridge/scripts/util/Response');
    r.renderJSON([{
        zip: session.custom.zipcode
    }]);
}

/*
 * Private helpers
 */

/**
 * Looks up stores based on the zipcode given and the site prefernces that refer
 * to the radius and units of distance.
 */
function lookupByZipCode() {
    var nearestStores = dw.catalog.StoreMgr.searchStoresByPostalCode(
        dw.system.Site.getCurrent().getCustomPreferenceValue('countryCode').value,
        request.httpParameterMap.zipCode.value,
        'mi',
        Number(dw.system.Site.getCurrent().getCustomPreferenceValue('storeLookupMaxDistance').value));

    session.custom.zipcode = request.httpParameterMap.zipCode.value;

    return {
        Stores: nearestStores.keySet(),
        StoresCount: nearestStores.size()
    };
}

/**
 * Assumes that the pid refers to a product line item instead of a product
 * object for determining a product's availability.
 */
function getProductLineItem(uuid) {
    var Cart = app.getModel('Cart');
    var cart = Cart.get();
    if (cart) {
        for (var i = 0; i < cart.object.productLineItems.length; i++) {
            var lineItem = cart.object.productLineItems[i];

            if (lineItem.UUID === uuid) {
                return lineItem;
            }
        }
    }

    return null;
}

/**
 * Sets the store and its inventory list for the given line item.
 *
 * @param {dw.order.Basket} basket      The current basket.
 * @param {String} liUUID      The UUID of the line item.
 * @param {String} storeId     The ID of the store.
 * @param {String} storepickup
 * @transaction
 */
function setStoreInLineItem(basket, liUUID, storeId, storepickup) {
    var args = {};
    var lineItemItr = basket.allProductLineItems.iterator();
    var productLineItem;

    dw.system.Transaction.wrap(function () {
        while (lineItemItr.hasNext()) {
            productLineItem = lineItemItr.next();
            if (productLineItem.UUID === liUUID) {
                if (storepickup.equalsIgnoreCase('true')) {
                    if (productLineItem.product.custom.availableForInStorePickup) {
                        if (!empty(storeId)) {
                            var store = dw.catalog.StoreMgr.getStore(storeId);
                            if (!empty(store) && !empty(store.custom.inventoryListId)) {
                                var storeinventory = dw.catalog.ProductInventoryMgr.getInventoryList(store.custom.inventoryListId);
                                if (!empty(storeinventory)) {
                                    if (!empty(storeinventory.getRecord(productLineItem.productID)) && storeinventory.getRecord(productLineItem.productID).ATS.value >= productLineItem.quantityValue) {

                                        productLineItem.custom.fromStoreId = store.ID;
                                        productLineItem.setProductInventoryList(storeinventory);

                                    } else {
                                        args.ErrorCode = 'Not available in that quantity';
                                        return;
                                    }
                                } else {
                                    args.ErrorCode = 'Store inventory list does not exist';
                                    return;
                                }
                            } else {
                                args.ErrorCode = 'Store object not available';
                                return;
                            }
                        } else {
                            args.ErrorCode = 'No store ID available';
                            return;
                        }
                    } else {
                        args.ErrorCode = 'Product not in store available';
                        return;
                    }
                } else {
                    productLineItem.custom.fromStoreId = '';
                    productLineItem.setProductInventoryList(null);
                    //Loop over the shipments to find the one with out instore to assign it to
                    //productLineItem.shipment = null;
                    //var shipmentToDelete
                    //check if the default shipment is instore or not
                    productLineItem.setShipment(basket.getDefaultShipment());
                }
                break;
            }
        }
    });

    return args;
}

/**
 * Gets store availability for the given product.
 *
 * @param  {dw.util.Collection} stores          The stores.
 * @param  {dw.catalog.Product} product         The product to get the availability for.
 * @param  {dw.order.ProductLineItem} productLineItem The line item for the product.
 * @return {dw.util.HashMap}                 The availability map.
 */
function getStoreAvailabilityMap(stores, product, productLineItem) {
    var storesItr = stores.iterator();
    var store = null;
    var storeAvailabilityMessage = '';
    var storeAvailabilityMap = new dw.util.HashMap();

    while (storesItr.hasNext()) {

        store = storesItr.next();
        storeAvailabilityMessage = getStoreAvailabilityMessage(store, product);
        // the inventory check is for a pli consider the qty value for available stores
        if (productLineItem) {
            var storeinventory = dw.catalog.ProductInventoryMgr.getInventoryList(store.custom.inventoryListId);
            if (!empty(storeinventory.getRecord(productLineItem.productID)) && storeinventory.getRecord(productLineItem.productID).ATS.value >= productLineItem.quantityValue) {
                storeAvailabilityMap.put(store.ID, storeAvailabilityMessage);
            }
        } else {
            storeAvailabilityMap.put(store.ID, storeAvailabilityMessage);
        }
    }

    return storeAvailabilityMap;
}

/**
 * Get the availability message for a given store and product.
 * @param  {dw.catalog.Store} store - The store.
 * @param  {dw.catalog.Product} product - The product.
 * @return {String}         The localized message.
 */
function getStoreAvailabilityMessage(store, product) {
    var storeInventoryListId = store.custom.inventoryListId;
    var productInventoryList = null;
    var productInventoryrecord = null;
    var availabilityMessage = dw.web.Resource.msg('cart.store.availableinstore','checkout',null); //"In stock"

    // check for Inventory Availability
    if (storeInventoryListId !== null) {
        productInventoryList = dw.catalog.ProductInventoryMgr.getInventoryList(storeInventoryListId);
        if (productInventoryList !== null) {
            productInventoryrecord = productInventoryList.getRecord(product.ID);
            if (productInventoryrecord !== null) {
                if (productInventoryrecord.ATS.value >= 1) {
                    //Instock
                    availabilityMessage = dw.web.Resource.msg('cart.store.availableinstore','checkout',null); //"In stock"
                } else {
                    // not available as ATS is less than 1
                    availabilityMessage = dw.web.Resource.msg('cart.store.notavailable','checkout',null); //"Not Available"
                }
            } else {
                // not available as Inventory Record doesn't exist
                availabilityMessage = dw.web.Resource.msg('cart.store.notavailable','checkout',null); //"Not Available"
            }
        } else {
            // not available as Inventory List doesn't exist
            availabilityMessage = dw.web.Resource.msg('cart.store.notavailable','storepickup',null); //"Not Available"
        }
    } else {
        // not available as Store Inventory is not set
        availabilityMessage = dw.web.Resource.msg('cart.store.notavailable','storepickup',null); //"Not Available"
    }

    return availabilityMessage;
}

/*
 * Web exposed methods
 */
/** @see module:controllers/StoreInventory~SetStore */
exports.SetStore = guard.ensure(['get'], setStore);
/** @see module:controllers/StoreInventory~ShowSelectedStoreCore */
exports.ShowSelectedStoreCore = guard.ensure(['get'], showSelectedStoreCore);
/** @see module:controllers/StoreInventory~ShowAvailableStores */
exports.ShowAvailableStores = guard.ensure(['get'], showAvailableStores);
/** @see module:controllers/StoreInventory~CartSetZipCodeCore */
exports.CartSetZipCodeCore = guard.ensure(['get'], cartSetZipCodeCore);
/** @see module:controllers/StoreInventory~SetZipCodeCore */
exports.SetZipCodeCore = guard.ensure(['post'], setZipCodeCore);
/** @see module:controllers/StoreInventory~SetStoreCore */
exports.SetStoreCore = guard.ensure(['get'], setStoreCore);
/** @see module:controllers/StoreInventory~Inventory */
exports.Inventory = guard.ensure(['get'], inventory);
/** @see module:controllers/StoreInventory~SetPreferredStore */
exports.SetPreferredStore = guard.ensure(['post'], setPreferredStore);
/** @see module:controllers/StoreInventory~GetPreferredStore */
exports.GetPreferredStore = guard.ensure(['get'], getPreferredStore);
/** @see module:controllers/StoreInventory~SetZipCode */
exports.SetZipCode = guard.ensure(['post'], setZipCode);
/** @see module:controllers/StoreInventory~GetZipCode */
exports.GetZipCode = guard.ensure(['get'], getZipCode);