Product Details Page Architecture

This topic applies to

Applies to

SuiteCommerce Advanced

This section applies to the Elbrus release of SuiteCommerce Advanced and later. The following architecture is not backwards compatible with previous versions of SuiteCommerce Advanced.

The product details page (PDP) provides shoppers with detailed information about a product and lets shoppers add items to their cart. To ensure a positive user experience, the PDP needs to create intuitive interactions and return information from the server quickly and efficiently. The architecture of the PDP is designed to accommodate these challenges. Communication of item details is accomplished through the Items and Product modules. This unifies the data structure governing order management.

The following diagram details the architecture behind the PDP and how user selections interact with NetSuite.

  1.  Logic in the Item module retrieves read-only data from NetSuite. Item.Model retrieves data about an item via the Search API. This is a client-side, read-only instance of an item record in NetSuite. Item.Option receives read-only information about an option associated with the item. Item.Option.Collection is a collection of these possible options.

    Note

    The Item.Model is the read-only data from NetSuite used by the Product.Model to validate the user selection. Providing a read-only version of the Item ensures that the original backend data cannot be accidentally overridden.


  2. Product.Model contains an Item.Model associated with an item to display in the PDP. Product.Option.Model contains editable instances of the related Item.Option models, and Product.Option.Collection is a collection of these options.

  3. The views and child views in the ProductDetails module use the data in the Product module to render the PDP. ProductDetails.Base.View contains the abstract view from which the Full PDP and Quick View views inherit.

  4. The views and child views in the ProductViews module use the data in the Product module to render price and item options in the PDP.

    Note

    To enhance extensibility, SCA uses two different item option templates, one for the PDP and another for search results (Facets).


  5. When the user interacts with the PDP to select options, Product.Model validates the data against the Item.Model that each Product.Model contains. This is why the Product.Model contains a Item.Model and why the Item.Model is a client-side representation of the backend record.

  6. When the user chooses valid options and clicks Add To Cart, the following conversions occur:

    • Product.Option.Model gets converted into Transaction.Line.Option.Model

    • Product.Option.Collection gets converted to Transaction.Line.Option.Collection

    • Product.Model gets converted into a Transaction.Line.Model

    The Transaction.Model contains a collection of transaction lines, which each include an item and selected options. Transaction.Line.Option.Model contains one of the selected option, and Transaction.Line.Option.Collection is a collection of all selected options.

  7. The ProductLine module contains views that are shared between the PDP, Checkout, and My Account applications.

  8. The Transaction module uses views in the Transaction.Line.Views module to render transaction details in the cart and in other modules.

Note

Item.KeyMapping.js is part of the Item Module. This file contains mapping definitions of what is returned by the Search API. For example, if you want to set the name of items for data stored in a custom field instead of the default Display Name field, you extend the mapping in the Item.KeyMapping file, as opposed to customizing each instance across the entire code base.


Configuration

To configure the Product Details Page, you must have the SuiteCommerce Configuration bundle installed. Refer to the correct section for details on configurable properties as described below.

To configure the Product Details Page:

  1. Select the domain to configure at Setup > SuiteCommerce Advanced > Configuration.

  2. In the SuiteCommerce Configuration record, navigate to the Shopping Catalog tab and the appropriate subtab:

  3. Save the SuiteCommerce Configuration record to apply changes to your site.

Code Examples

Add an Item to the Cart

The following example of a custom model adds an item to the cart. Product.Model contains the item via the fetch method. Based on the internal ID of the item, this code then sets the quantity and item options and adds the product to the cart via the LiveOrderModel.

define('AddToCart',
[
        'Product.Model'
    ,   'LiveOrder.Model'
],
function(
        ProductModel
    ,   LiveOrderModel
)
{
    var my_item_internal_id = '40'
    ,   quantity = 10
    ,   product = new ProductModel()
    // load the item.
    product.getItem().fetch({
        data: {id: my_item_internal_id}
    }).then(function (){
        // set quantity
        product.set('quantity', quantity);
        // set options
        product.setOption('option_id', 'value');
        // add to cart
        LiveOrderModel.getInstance().addProduct(product);
    });
});

Validate an Item for Purchase

All shopping, wishlist, and quote item validation is centralized in the Product.Model. You can validate per field and developers can override the default validation per field.

The following customization uses a generic areAttributesValid method to validate per field. In this case options and quantity, as opposed to validating the entire set. This method uses a pre-set default validation to validate the specified fields. The generateOptionalExtraValidation method overrides the default validation for the any fields. In this case, quantity. If the values are not valid, the code introduces an alert message. Otherwise, the code adds the product to the cart.

Note

The default validation is set in Product.Model.js.


define('ValidateAddToCart'
,   [
        'LiveOrder.Model'
    ]
,   function (
        LiveOrderModel
    )
{
    function generateOptionalExtraValidation() {
        return {
            'quantity': function (){}
        }
    }
    var product = obtainSomeProduct(/*...*/);
    var cart = LiveOrderModel.getInstance();
    if (!product.areAttributesValid(['options','quantity'], this.generateOptionalExtraValidation()))
    {
        alert('Invalid Product!')
    }
    else
    {
        cart.addProduct(product);
    }
});

Add Item to Wishlist

The following customization gets new item data from the product and the correct product list. It then adds the product line to the list and alerts the user upon successful validation.

define('ValidateForWishList'
,   [
        'ProductList.Item.Model'
    ]
,   function (
        ProductListItemModel
    )
{
    var getNewItemData = function (product, productList)
        {
            var product_list_line = ProductListItemModel.createFromProduct(product);
            product_list_line.set('productList', {
                id: productList.get('internalid')
            ,   owner: productList.get('owner').id
            });
            return product_list_line;
        };
 
    var doAddProductToList = function (product, productList, dontShowMessage)
        {
            var product_list_line_to_save = getNewItemData(product, productList);
            product_list_line_to_save.save(null, {
                validate: false
            ,   success: function (product_list_line_to_save_saved)
                {
                    alert('Product Added!');                   
                }
            });
        };
    var product = getSomeProduct(/*...*/)
    ,   product_list = getSomeProduct(/*...*/);
    doAddProductToList(product, product_list);
});

Get Options

The following customization returns all valid options for an item or matrix item. It then determines which item options are valid. Only valid options will render as selectable options in the browser.

define('ReadProductOptions'
,   [
        'underscore'
    ]
,   function (
        _
    )
{
    var showFirstOptionWithValidValues = function (product)
    {
        //Collection of product.options
        var options_to_render = product.get('options');
        options_to_render.each(function (option)
        {
            //each option is a Model
            if (option.get('isMatrixDimension'))
            {
                var valid_values_for_selected_option = product.getValidValuesForOption(option);
                _.each(option.get('values'), function (value)
                {
                    value.set('isAvailable', _.contains(valid_values_for_selected_option, value.get('label')));
                });
            }
        });
        var first_valid_option = options_to_render.find(function (option)
            {
                return _.some(option.get('values'), function (value) { return value.get('isAvailable'); });
            });
        var selected_option = product.get('options').findWhere({cartOptionId: first_valid_option.get('cartOptionId')});
        //Note that we are always backbone Model and the value of the set option contains all details of the set value (label and internalid)
        alert('First Option with value values: ' + first_valid_option.get('cartOptionId') + ' set value: ' + selected_option.get('value').label)
    };
    showFirstOptionWithValidValues(getSomeIProduct(/*...*/));
});