Post Featured Image

Extension Spotlight: Grid Order

As this post is about a SuiteCommerce extension, it is only appropriate for sites running Aconcagua or newer.

In the first of a new series, I want to shed some light on some of the extensions the NetSuite commerce team have been building. I think when it comes to customizing your site, it is easy to overlook some of the pluggable functionality we offer as it falls outside of the regular source code you may be used to seeing.

Extensions can be installed on any site as long as they are running the SuiteCommerce extension manager, and have access to the extensibility API code. This means that they are only appropriate for sites running Aconcagua or newer.

On a personal note, I'm a big fan of NetSuite-built extensions: not only do they offer new features and functionality, available to non-technical people at the clicks of a few buttons, they are also a testament to how flexible the SuiteCommerce platform is. We spend a lot of time on this blog talking about customizations, and many of our extensions solve specific problems or meet use cases that have come from conversations we've had with our customers.

I also want to highlight how important it is to review our extensions. It is not uncommon for me and my colleagues to receive requests for help customizing a site only to find that they could have been accomplished by using some of our wonderful pre-built extensions. In other words, if you're thinking about writing a new customization for your site, take a look at our extensions first (eg on SuiteApp.com)

Finally, as developers I think you can also learn a lot by looking at the code for our extensions. While we don't publish the full source in public repos, the JavaScript for extensions is clearly visible on the frontend of the site simply by inspecting it in your browser's developer console.

Summary

This week, we're going to be look at an interesting extension for product details pages: grid order.

This extension changes the appearance of some or all product detail pages for matrix items. Rather than shoppers having to input individual selections for all the different matrix combinations they want, they can complete a single form marking all of the combinations and quantities and add them to the cart at once.

Sometimes this functionality is referred to as 'matrix multi-add'.

As you can see in the gif above, it transforms a product detail matrix into a large form. Accordingly, it is best suited for sites and shoppers where it is common to order significant quantities of multiple variants of an item at once. In that respect, it is very much an excellent feature for B2B sites.

In combination with the core functionality, there are a number of configuration options that, in total, enable shoppers to:

  • Interact with all item options in a grid shaped form
  • See clearly each child item's availability including live stock levels
  • Set desired quantities at child-item level
  • Verify, at a glance, the order amounts
  • Add all desired child items to the cart with a single click

There are also additional use cases, depending on other SuiteCommerce features and extensions you may have enabled, such as:

Also bear in mind that there are some limitations (at least at the time of writing). It does not support:

  • The add to wishlist or add to quote features
  • Matrix items with more than two selectable dimensions/options
  • Matrix items with a mandatory item option on the parent item

Configuration

Out of the box, there are a number of configurable options that can be broken down into two main areas:

  1. Domain-level
  2. Item-level

Domain-Level

These configuration options are added to the SuiteCommerce configuration record in the Extensions > Grid Order subtab. The majority of the fields determine text strings for translation, but there are two crucial checkboxes: Activate For All Matrix Items and Activate For Mobile.

The first sets the default behavior for the site. When enabled, all product detail pages will be changed to use grid order, unless you override them on an item-level. The second enables the functionality for mobile users — depending on your tastes and the perceived user experience, you may want to not enable for mobile users if you're worried about the usability of the functionality on small screens.

Item-Level

You can see the item-level options in each item's record at the bottom, in the SuiteCommerce Extensions section.

The most important option is the Grid Order Behavior dropdown, which determines whether to show the grid or not for this specific item. The three options let you decide whether to always show it, always hide it, or to follow the domain-level behavior. Therefore, you have very granular control of your items and whether to show them or not.

Next, the Show Stock Quantities option can help you cut down on some noise by hiding the stock counts that appear underneath each input field.

Finally, if you have the stock notifications extension then you can enable the Activate Stock Notifications option to generate a Notify Me link for a valid and not back-orderable, but out of stock, matrix item.

Code Examination

I'm not going to go through all of the files that make up this extension, but I do want to highlight some interesting tidbits that may help you in your own extension development. The first thing I want to point out is how the module structure within the extension looks.

While you will not be able to download the full source code, you can inspect it in your browser after it has been activated on your site. The class names start with SuiteCommerce.GridOrder.

Module Structure

With that said, this one is a bit difficult to illustrate without source code access, so I hope I can describe it well enough for you to understand.

Best practices for extension development is something we have covered before but worth reiterating due to having a great example. Remember: you are not bound to only having one module in your extension. In fact, we advocate splitting them up when necessary, and the grid order extension illustrates that.

In the Modules directory, there are three folders: GridOrder, Instrumentation, and Main. For this extension, we store the feature code in GridOrder and the entry points and utility code in Main.

The main module frontend entry point file is the entry point that the extension developer tools recognize, and it all does is call the mountToApp of the grid order module. However, should the developers which to expand this functionality, they could add in additional calls into the main entry point file, without conceptually changing the nature of the grid order entry point file. In total, this helps keep the entire extension flexible.

Removing Child View Instances

The second thing that I have found interesting is how we address an issue with the removal of child views.

When you enable this functionality on an item, grid order takes ownership of a number of the PDP's features and therefore removes a number of child views:

  • Product.Options
  • Product.Price
  • Quantity
  • AddToProductList
  • ProductDetails.AddToQuote
  • MainActionView
  • Quantity.Pricing
  • Product.Stock.Info

Now, a lot of you smarty-pants out there will be thinking something like, "Well, this is easy — just use the removeChildView() method on the extensibility API". However, if you've ever tried to use this method conditionally then you'll know that this doesn't quite work that way.

removeChildView() removes child views on class basis, not an instance basis.

Consider the following simple example:

if (PDP.getItemInfo().item.custitem_hideprice)
{
  PDP.removeChildView('Product.Price')
}

Do you think it will remove the price child view only on PDPs that have our specified custom field set to true on the item record, or do you think it will remove it from all PDPs as the first time it triggers? The answer is the latter.

This isn't a bug, however, this is by design. In other words, it is not designed to work on individual pages, it is designed to work across all pages. Thus, this method isn't appropriate for what we want to achieve.

What's interesting is how the developers solved this problem in their extension. When I saw it, I thought it was rather clever and novel. However, when I talked to them about it, they weren't particularly happy with how they did it as they had to use jQuery.

When I asked whether this should be considered 'best practice', the answer was a measured no: it's not a great way of doing it, but they think it's the best one they have at the moment. They had tried experimenting with what's returned by a view's childViewInstances property, to see if they could prevent certain views from rendering, but they found this a risky endeavour. In particular, from an extension point of view, they were concerned about future compatibility and wanted something that was robust from this point of view; and, well, using jQuery to delete blocks of rendered code is definitely reliable. (But it sure would be nice if they could be removed before rendering.)

Anyway, to see the code in action, you'll need to look in GridOrder.js, where you'll see this:

removePDPChildViews: function removePDPChildViews(pdp) {
  pdp.on('afterShowContent', function afterShowContent() {
    var item = pdp.getItemInfo().item;
    var itemModel = new ItemModel(item);

    if (itemModel.isValidMatrix()) {
      _.each(childViewsToRemove, function eachChildrenViewsToRemove(dataView) {
        jQuery('.product-details-full-main-content [data-view="' + dataView + '"]').remove();
      });
    }
  });
},

Further up in the module, we define an array of child views we want to remove. Now, in this bit of code, you see that we iterate through each one, using the names as part of the jQuery selector. For example, if we want to remove the Product.Price view, then we search through the DOM for elements that match: .product-details-full-main-content [data-view="Product.Price"]. Once we have that, we just call remove(), which deletes the element.

Again, this isn't great practice but we know that it is effective and reliable. Ideally, we would prevent the child views from rendering in the first place, but there wasn't a good way of doing this.

Matrix Child Item URL Parameters

Finally, there's a little bit of code below this called clearSelectedMatrixChildOptions(). There isn't a lot to say about this but it is a result of some good testing. THe team noticed that when you visited a grid order PDP with some matrix item options set in the URL as a parameters, there were some issues and conflicts. So, quite simply, they wrote some code so that when the extension loads, the PDP component's setOption() method is triggered twice to set the 'row' and 'column' options to null values.

This avoids issues where the PDP tries to use those values, for example to set the values in the model.

Links