Creating Products - Part 3

Managing Product Options

Introduction

Welcome to the third installment in our series about creating and importing products into Liferay DXP.  In this series we're taking a step-by-step approach that covers all of the preliminary steps you should be taking before you import products from an external system such as an PIM or ERP.  Previously we've talked about configuring your categories and your specifications.  In this installment we're going to take a closer look at product options.  

Product options, sometimes called product variants, allow you to customize products.  In some ways, they are similar to product specifications, since they often describe some aspect or attribute of the product.  However, with a product option, we're dealing with aspects of a product that can be changed.  Some examples of this would be the size of a t-shirt or the color of a jacket.  By changing these values you aren't defining a new product, but rather adding additional information about the product you are ordering or in some cases identifying a new Stock Keeping Unit or SKU.  

A Stock Keeping Unit or SKU is an unique code that identifies a specific product option or variant.  SKUs allow for tracking inventory and assigning prices.  

Options as Templates

One important thing to note about options is that in this blog post, what we're really working with are option templates.  When a product is created and one of these options is applied, the details of the option are applied to the product and they are no longer connected to the option template.  This means that any future changes made to the option template will not be applied to existing products and that any changes made to the option at the product level will not be applied to the option templates.

Sometimes this disconnect works to your advantage.  For example, when working with color, you may determine that the majority of your products come in red, blue, and green so you might create an option template with those values.  But for one particular product it is available in red, blue, and yellow.  When you first apply the color option to your product the default colors are applied but you can then delete the green option and create a new color value for yellow  Nothing you do at the product level will have any impact so your default colors of red, blue, and green remain unchanged. Of course, sometimes this can also be a disadvantage.  Let's imagine we are working with a new supplier who is able to provide us with products in a new color, let's say black.  We can update our option values to include red, blue, green, and black and all new products will benefit, but none of our existing products will have black as an option.  The only way to have those products available in black is to update each product individually.  Now that actually sounds worse than it is because in reality you would need to update each of those products anyway because you'd also need to add an additional SKU or SKUs and possibly add additional images.

Option Types

Liferay provides many different option types that should cover just about any use case you might run up against including text, single and multi select, dates and boolean.  I'm not going to go through every single option type in this blog, since the Liferay Documentation team has done a great job documenting them in an article about Using Product Options.   If you find that the out-of-the-box product options aren't sufficient for your use case, it is possible to extend Liferay to provide new options, but that's outside the scope of this blog series so for now let's focus on the product options that are relevant for our new line of shop wear.  

For our extended example we'll be introducing several new product options, in addition to the product option that was created by the Minium accelerator.  The fist two options will be to allow customers to select the color and the size of the garment and for this we'll use the single selection option type.  The third option, custom text, will be used to add some text that will be embroidered or printed on some of the shop wear items and for that we'll use the text option type.  

Creating Options

Creating Options through the UI is very straightforward.  Start from the Global Menu > Commerce - Options and click the Add Options button.  There are three required fields that are needed to create a new option.  Name, Option Field Type, and Key.  The Name field will be displayed in the UI and can be localized later if necessary.  The Option Type, we've already mentioned above and the Key is a unique identifier.  Liferay will suggest a Key based on the Name entered but you are free to change the Key if you'd like. And that's it, that's all it takes to create a new Option through the UI. 


 

A couple of things to note about product options:  
1) Unlike product specifications, product options do support an External Reference Code or ERC which should be used for integrating with external systems
2) Unlike categories, the ERC is exposed through the UI so it can be updated manually if necessary.  

As you might expect, there are a few other configurations that are available to us and once you click Submit you'll see all of the options available.  


 

This is the screen that allows you to provide localized values for Name as well as a localized Description.  We can also decide if we want our option to appear in the Option Facet in our search experiences.   This would be very useful for options such as Color or Size, but wouldn't make sense for a free form text option for something like monogramming.  We can also select if option will be required before adding the item to your cart and finally we can decide if this option is a SKU Contributor.  SKU Contributors are options that are tied to different SKUs.  Again, think of options such as Color or Size.  Each of these will correspond to a different item on a shelf in the store of the warehouse, but an option such as monogramming wouldn't typically be tied to a different SKU.  

Importing Options

As I've mentioned previously when working with Liferay's Headless APIs, the easiest way to get started is with Liferay's API Explorer.  To do this, be sure you are logged in as an Administrator and navigate to the Liferay API Explorer (e.g. http://localhost:8080/o/api).  We're going to be working with the Commerce Admin Catalog API (/headless-commerce-admin-catalog/v1.0).  Thankfully the Product Option related endpoints are all very intuitively named and we'll start in the Option section.  


If we are creating or importing a single Option we can POST to the /o/headless-commerce-admin-catalog/v1.0/options endpoint or if we are going to be working with multiple options we can use the /o/headless-commerce-admin-catalog/v1.0/options/batch endpoint.  In either case, I recommend you start with a GET against the /o/headless-commerce-admin-catalog/v1.0/options endpoint to understand what the JSON payload will need to look like to create a new option.   If you're using the Minium Accelerator as your starting point, there is already one Option that was created, for Package Quantity. From the JSON that's returned we can extract the single option from the items array: 

    {
      "actions": {
        "get": {
          "method": "GET",
          "href": "https://prd-lctlxccommerce.us.demo.lxc.liferay.com/o/headless-commerce-admin-catalog/v1.0/options/7233457"
        },
        "update": {
          "method": "PATCH",
          "href": "https://prd-lctlxccommerce.us.demo.lxc.liferay.com/o/headless-commerce-admin-catalog/v1.0/options/7233457"
        },
        "delete": {
          "method": "DELETE",
          "href": "https://prd-lctlxccommerce.us.demo.lxc.liferay.com/o/headless-commerce-admin-catalog/v1.0/options/7233457"
        }
      },
      "customFields": [],
      "description": {},
      "externalReferenceCode": "97e50025-f0f9-ee42-c8e9-6fdfd6ca1ab9",
      "facetable": true,
      "fieldType": "select",
      "id": 7233457,
      "key": "package-quantity",
      "name": {
        "en_US": "Package Quantity"
      },
      "required": true,
      "skuContributor": true
    }

This is a pretty good example because aside from the name and ERC it's very close to what we will want to configure for both size and color.  We can ignore the actions, customFields, and id fields, and we can then update what's left. 

For the third option, the custom text option we are going to need to use a different field type.  In this case could probably guess what value we'll need to supply but if we want to be certain we're using a valid fieldType, Liferay's API Explorer makes it very easy to examine the schema.  First, expand the POST /1.0/options endpoint and the select Schema and then select Option > fieldType.  You'll see the valid options for fieldType are checkbox, checkbox_multiple, date, numeric, radio, select, select_date, or text.  

Since we have 3 different options we want to create, we'll use the batch endpoint (/o/headless-commerce-admin-catalog/v1.0/options/batch ) to create them in a single call.  

[
  {
    "description": {
      "en_US": "Select from available colors",
      "es_ES": "Seleccione entre los colores disponibles."
    },
    "externalReferenceCode": "ext-color-option",
    "facetable": true,
    "fieldType": "select",
    "key": "color",
    "name": {
      "en_US": "Color",
      "es_ES": "Color"
    },
    "required": true,
    "skuContributor": true
  },
  {
    "description": {
      "en_US": "Select from available sizes.",
      "es_ES": "Selecciona entre tallas disponibles."
    },
    "externalReferenceCode": "ext-size-option",
    "facetable": true,
    "fieldType": "select",
    "key": "size",
    "name": {
      "en_US": "Size",
      "es_ES": "Talla"
    },
    "required": true,
    "skuContributor": true
  },
  {
    "description": {
      "en_US": "Enter text for personalization",
      "es_ES": "Introduzca el texto para personalizarlo"
    },
    "externalReferenceCode": "ext-custom-text-option",
    "facetable": false,
    "fieldType": "text",
    "key": "custom-text",
    "name": {
      "en_US": "Custom Text",
      "es_ES": "Texto Personalizado"
    },
    "required": false,
    "skuContributor": false
  }
]

Remember that when you use the Batch endpoints you won't get immediate feedback on the status of your request (unless you have malformed JSON) so you can use the id value that was returned with the /headless-batch-engine/v1.0/import-task/{id} endpoint or you also just take a quick look at the Options configuration through the browser and confirm that you now have three new options.  

Option Values

As we have alluded to earlier, when setting up options in Liferay DXP we are really setting up option templates that will be applied to products when they are created or imported.  While we're doing that we can also provide default values for those options.  For example, for size we might determine that the majority of our shop wear are offered in small, medium, and large so those might be the defaults and then for any item that comes in extended sizes, we could add those directly to the product.  Or we might opt to cover all our bases and start with extra small, small, medium, large, and extra large and then just delete the options that aren't needed, it's really up to you. 

One thing to note is that these option values are a bigger concern when creating products through the UI.  If we're importing products from an external system, we will typically be designating the option values as part of that import and the default values will not be used  

Creating Option Values

To add new Option Values through the UI, you'll need to use the Add Option Value Template button and then provide the Name, Key, and Position values.  The Name is what will be displayed in the UI.  In this view you can't provide localized values for the name but once you've saved the initial values you'll have the opportunity to come back and provided localized values.  The key is how you can interact with this value programmatically and the position is used by some of the out of the box UI components when displaying the Option Values. 


 

To determine what our Option Values need to look like when adding them through the API we actually have a couple of options.  The first option is to use the Option Value endpoints which allow us to view the Option Values individually by Id or ERC or to view all Options Values by Option.  


If we Examine the Option Values that the Minium accelerator created for the Package Quantity, we'll see an array of Items and examining one of them more closely we end up with:  

{
      "actions": {
        "get": {
          "method": "GET",
          "href": "http://localhost:8080/o/headless-commerce-admin-catalog/v1.0/optionValues/{id}"
        },
        "update": {
          "method": "PATCH",
          "href": "http://localhost:8080/o/headless-commerce-admin-catalog/v1.0/optionValues/{id}"
        },
        "delete": {
          "method": "DELETE",
          "href": "http://localhost:8080o/headless-commerce-admin-catalog/v1.0/optionValues/{id}"
        }
      },
      "customFields": [],
      "externalReferenceCode": "0b771527-9bb4-e9a2-a78e-f6879bc6fed3",
      "id": 7233232,
      "key": "112",
      "name": {
        "en_US": "112"
      },
      "priority": 4
}

Again to create our payload to POST, we can ignore the actions, customFields, and id and we would come up with something like this for creating a new color option, which we could POST to the /o/headless-commerce-admin-catalog/v1.0/options/by-externalReferenceCode/ext-color-option/optionValues or /o/headless-commerce-admin-catalog/v1.0/options/{optionId}/optionValues endpoint.

{
  "externalReferenceCode": "EXT-OPT-VAL-RED",
  "key": "red",
  "name": {
    "en_US": "Red",
    "es_ES": "Rojo"
  },
  "priority": 10
}

The only problem with this approach is that if we have a lot of different Option Values, we'll have to make a lot of calls to create each of them.  Wouldn't it be great if there was a way we could simplify all of these calls and create all of the Options and Option Values all at once, in a single call?  Well, there is.  We can use the very same Batch endpoint we used earlier (/o/headless-commerce-admin-catalog/v1.0/options/batch ).  

[
  {
    "description": {
      "en_US": "Select from available colors",
      "es_ES": "Seleccione entre los colores disponibles."
    },
    "externalReferenceCode": "ext-color-option",
    "facetable": true,
    "fieldType": "select",
    "key": "color",
    "name": {
      "en_US": "Color",
      "es_ES": "Color"
    },
    "optionValues": [
      {
        "externalReferenceCode": "EXT-OPT-VAL-RED",
        "key": "red",
        "name": {
          "en_US": "Red",
          "es_ES": "Rojo"
        },
        "priority": 10
      },
      {
        "externalReferenceCode": "EXT-OPT-VAL-BLUE",
        "key": "blue",
        "name": {
          "en_US": "Blue",
          "es_ES": "Azul"
        },
        "priority": 20
      },
      {
        "externalReferenceCode": "EXT-OPT-VAL-GREEN",
        "key": "green",
        "name": {
          "en_US": "Green",
          "es_ES": "Verde"
        },
        "priority": 30
      }
    ],
    "required": true,
    "skuContributor": true
  },
  {
    "description": {
      "en_US": "Select from available sizes.",
      "es_ES": "Selecciona entre tallas disponibles."
    },
    "externalReferenceCode": "ext-size-option",
    "facetable": true,
    "fieldType": "select",
    "key": "size",
    "name": {
      "en_US": "Size",
      "es_ES": "Talla"
    },
    "optionValues": [
      {
        "externalReferenceCode": "EXT-OPT-VAL-SMALL",
        "key": "small",
        "name": {
          "en_US": "Small",
          "es_ES": "Chica"
        },
        "priority": 10
      },
      {
        "externalReferenceCode": "EXT-OPT-VAL-MEDIUM",
        "key": "medium",
        "name": {
          "en_US": "Medium",
          "es_ES": "Mediana"
        },
        "priority": 20
      },
      {
        "externalReferenceCode": "EXT-OPT-VAL-LARGE",
        "key": "large",
        "name": {
          "en_US": "Large",
          "es_ES": "Grande"
        },
        "priority": 30
      }
    ],
    "required": true,
    "skuContributor": true
  },
  {
    "description": {
      "en_US": "Enter text for personalization",
      "es_ES": "Introduzca el texto para personalizarlo"
    },
    "externalReferenceCode": "ext-custom-text-option",
    "facetable": false,
    "fieldType": "text",
    "key": "custom-text",
    "name": {
      "en_US": "Custom Text",
      "es_ES": "Texto Personalizado"
    },
    "required": false,
    "skuContributor": false
  }
] 

One More Thing...

These are some simple of examples of the types of Options we might choose to define for our commerce solution.  Another common use-case for Options is to create product bundles.  With a product bundle, you are combining several products together into a single offering for your customers, but it's an offering that provides customers with some choices.  A good example of a product bundle that we are all probably familiar with is a mobile phone bundle.  When we buy a new phone we are often presented with the option to select from a selection of phones, a data plan, and possibly other add-on products.  As you build your bundle, the price updates automatically to reflect the Options that you've selected.  Creating a product bundle is outside the scope of this blog post, but if you're interested in learning more about product bundles with Liferay you can check out the Creating Product Bundles article on Liferay Learn or leave me a comment and we can explore that topic in a future blog post.  

Conclusion

And just like that, we have our Options and Option Values defined and ready to go.  Hopefully you've learned a little more about Options along the way and have a better understanding of how to create and manage them through both the UI and the API.  

These Options and Option Values are really just the default values available to us as we create products but more can always be defined as we create or import our products.  The advantage of pre-defining these values is that we can be very deliberate in how we define them.  We can provide localized names and descriptions and we can determine if the Options are required or SKU contributors.  We can also determine if the Option should appear as an Option Facet when we are creating our search experiences. 

In our next installment, we're going to be talking about product images and attachments.  While it is possible to manage these directly as we create and import products, I'll make the case for why that might not be the best option and it might actually be better for you to create or import these independently, before you create or import your product.