Use Tealeaf to capture cart abandonment triggered by low inventory

By integrating WebSphere Commerce and Tealeaf Customer Experience on Cloud, we can begin tracking and understanding shopper behavior on your Aurora starter store. Tealeaf comes configured with default reports, workspaces, and events to help you analyze the behavior of your online shoppers. We can enhance the insights into our organization's web and mobile apps by building customized reports, workspaces, and events. This demonstration shows you how to build custom Tealeaf events to track cart abandonment that is triggered by low product inventory.

Cart abandonment is known problem in the online shopping industry, and it is not always obvious why a particular customer abandons an online shopping cart. With some minor customizations, we can begin tracking specific user interactions that might lead to cart abandonment, for example, an out of stock item. To capture this scenario, the demonstration describes how to hook Tealeaf JavaScript into specific Aurora store pages, and then create custom events to track cart abandonment due to a product that is out of stock. The implementation can be broken down into the following stages:


Updating Aurora starter store pages to send out of stock event to Tealeaf

  1. In your development environment, update the SelectedStoreList.jsp file to send an out of stock event to Tealeaf.

    1. Go to the following directory:

    2. Open the SelectedStoreList.jsp file for editing.

    3. Add the following code to send an out of stock event (around line 212 in the file) to Tealeaf as a custom event:

        <c:if test="${invStatus == 'Unavailable'}">
         <script>
        if (typeof TealeafWCJS != "undefined"){
        TealeafWCJS.logStoreOutOfStockCustomEvent("${productIDs}", "${physicalStores.PhysicalStore[i].uniqueID}");
        }
         </script>
         <div id="checkOutOfStockRefresh" style="display:none;">
                        if (typeof TealeafWCJS != "undefined"){
        TealeafWCJS.logStoreOutOfStockCustomEvent("${productIDs}",   "${physicalStores.PhysicalStore[i].uniqueID}");
        }
         </div>
        </c:if>

        <c:if test="${invStatus == 'Unavailable' ||  invStatus==’Backorderable’}">
         <script>
        if (typeof TealeafWCJS != "undefined"){
        TealeafWCJS.logStoreOutOfStockCustomEvent("${productIDs}", "${physicalStores.PhysicalStore[i].uniqueID}");
        }
         </script>
         <div id="checkOutOfStockRefresh" style="display:none;">
                        if (typeof TealeafWCJS != "undefined"){
        TealeafWCJS.logStoreOutOfStockCustomEvent("${productIDs}",   "${physicalStores.PhysicalStore[i].uniqueID}");
        }
         </div>
        </c:if>

      The following code snippet is an example of how the added code looks within the file:

        <c:when test="${!empty orderId}">
          <c:choose>
        	<c:when test="${invStatus != ''}">
        	  <img src="<c:out value='${jspStoreImgDir}images/' />${invStatus}.gif" ${storeText}" 
                  key='AVAILABILITY_${invStatus}_IMAGE'/>" />
        	  <fmt:message bundle="${storeText}" key="INV_STATUS_${invStatus}"/> 
        	  <c:if test="${invStatus == 'Unavailable'}">
        		<script>
        		if (typeof TealeafWCJS != "undefined"){
        		TealeafWCJS.logStoreOutOfStockCustomEvent("${productIDs}", "${physicalStores.PhysicalStore[i].uniqueID}");
        		}
        		</script>
        		<div id="checkOutOfStockRefresh" style="display:none;">
        		if (typeof TealeafWCJS != "undefined"){
        		TealeafWCJS.logStoreOutOfStockCustomEvent("${productIDs}", "${physicalStores.PhysicalStore[i].uniqueID}");
        		}
        		</div>
        	  </c:if>
               </c:when>
        	<c:otherwise>
        	  <fmt:message bundle="${storeText}" key="INV_INV_NA"/> 
        	</c:otherwise>
          </c:choose>
        </c:when>

        <c:when test="${!empty orderId}">
          <c:choose>
        	<c:when test="${invStatus != ''}">
        	  <img src="<c:out value='${jspStoreImgDir}images/' />${invStatus}.gif" ${storeText}" 
                  key='AVAILABILITY_${invStatus}_IMAGE'/>" />
        	  <fmt:message bundle="${storeText}" key="INV_STATUS_${invStatus}"/> 
        	  <c:if test="${invStatus == 'Unavailable' ||  invStatus==’Backorderable’}">
        		<script>
        		if (typeof TealeafWCJS != "undefined"){
        		TealeafWCJS.logStoreOutOfStockCustomEvent("${productIDs}", "${physicalStores.PhysicalStore[i].uniqueID}");
        		}
        		</script>
        		<div id="checkOutOfStockRefresh" style="display:none;">
        		if (typeof TealeafWCJS != "undefined"){
        		TealeafWCJS.logStoreOutOfStockCustomEvent("${productIDs}", "${physicalStores.PhysicalStore[i].uniqueID}");
        		}
        		</div>
        	  </c:if>    
               </c:when>
        	<c:otherwise>
        	  <fmt:message bundle="${storeText}" key="INV_INV_NA"/> 
        	</c:otherwise>
          </c:choose>
        </c:when>

    4. Save and close the file.

  2. Add a function to call logCustomEvent function from tealeafWC.js file.

    1. In your development environment, go to the following directory:

    2. Open the tealeafWC.js for editing.

    3. Add the following code to the file:

        logStoreOutOfStockCustomEvent:function(productId,      physicalStoreID) {
            var outOfStockObj = {};
            outOfStockObj.description= "Store Out of Stock error";
            outOfStockObj.productId = productId;
            outOfStockObj.physicalStoreID = physicalStoreID;					
            //alert(JSON.stringify(outOfStockObj, null, 4));
            TealeafWCJS.logStoreCustomEvent("WCStoreOutOfStock", outOfStockObj);
        
        },
        	
            logStoreCustomEvent:function(eventName, customEventObj) {
                if (typeof TLT != 'undefined') {
                        if(TLT.isInitialized()){
                               TLT.logCustomEvent(eventName, customEventObj);
                        }
                        else{				
                               setTimeout(function () { TealeafWCJS.logStoreCustomEvent(eventName, 
                                 customEventObj)}, 100);
                        }
        				
            }
        },

        logStoreOutOfStockCustomEvent:function(productId,      physicalStoreID) {
            var outOfStockObj = {};
            outOfStockObj.description= "Store Out of Stock error";
            outOfStockObj.productId = productId;
            outOfStockObj.physicalStoreID = physicalStoreID;					
            TealeafWCJS.logStoreCustomEvent("WCStoreOutOfStock", outOfStockObj);
        
        },
        	
            logStoreCustomEvent:function(eventName, customEventObj) {
                if (typeof TLT != 'undefined') {
                        if(TLT.isInitialized()){
                               TLT.logCustomEvent(eventName, customEventObj);
                        }
                        else{				
                               setTimeout(function () { TealeafWCJS.logStoreCustomEvent(eventName, 
                                 customEventObj)}, 100);
                        }
        				
            }
        },

      The following code snippet is an example of how the added code looks within the file:

        TealeafWCJS ={
        processDOMEvent:function(event){
        	if (typeof TLT != 'undefined') {
        		TLT.processDOMEvent(event);
        	}
        },
        rebind:function(id){
        	if (typeof TLT != 'undefined' && TLT.rebind) {
        	   var scope = dojo.byId(id);
        	   if (scope) {
        		   TLT.rebind(scope);
        	   }
        	}
        },
        logClientValidationCustomEvent:function( customMsgObj){
        	if (typeof TLT != 'undefined') {
        		if(TLT.isInitialized()){
        			TLT.logCustomEvent("WCClientValidation", customMsgObj);
        		}
        		else{			
        			setTimeout(function () { logClientValidationCustomEvent( customMsgObj)}, 100);
        		}
        	}
        },logStoreOutOfStockCustomEvent:function(productId, physicalStoreID) {
        	var outOfStockObj = {};
        	outOfStockObj.description= "Store Out of Stock error";
        	outOfStockObj.productId = productId;
        	outOfStockObj.physicalStoreID = physicalStoreID;					
        	//alert(JSON.stringify(outOfStockObj, null, 4));
        	TealeafWCJS.logStoreCustomEvent("WCStoreOutOfStock", outOfStockObj);
        },
        logStoreCustomEvent:function(eventName, customEventObj) {
        	if (typeof TLT != 'undefined') {
        		if(TLT.isInitialized()){
        			TLT.logCustomEvent(eventName, customEventObj);
        		}
        		else{				
        			setTimeout(function () { TealeafWCJS.logStoreCustomEvent(eventName, 
                               customEventObj)}, 100);
        		}
        			
        	}
        },
        createExplicitChangeEvent:function(id){
           if (typeof TLT != 'undefined') {
        		if (document.createEventObject && dojo.isIE < 9) { 				   
                          // for IE 
        			var evt=document.createEventObject();
        			dojo.byId(id).fireEvent("onchange",evt);
        		} else {
        			var evt=document.createEvent("HTMLEvents");
        			evt.initEvent("change", true, false);
        			dojo.byId(id).dispatchEvent(evt);	
        		} 
        	}		
        }
        }

        TealeafWCJS ={
        processDOMEvent:function(event){
        	if (typeof TLT != 'undefined') {
        		TLT.processDOMEvent(event);
        	}
        },
        rebind:function(id){
        	if (typeof TLT != 'undefined' && TLT.rebind) {
        	   var scope = dojo.byId(id);
        	   if (scope) {
        		   TLT.rebind(scope);
        	   }
        	}
        },
        logClientValidationCustomEvent:function( customMsgObj){
        	if (typeof TLT != 'undefined') {
        		if(TLT.isInitialized()){
        			TLT.logCustomEvent("WCClientValidation", customMsgObj);
        		}
        		else{			
        			setTimeout(function () { logClientValidationCustomEvent( customMsgObj)}, 100);
        		}
        	}
        },logStoreOutOfStockCustomEvent:function(productId, physicalStoreID) {
        	var outOfStockObj = {};
        	outOfStockObj.description= "Store Out of Stock error";
        	outOfStockObj.productId = productId;
        	outOfStockObj.physicalStoreID = physicalStoreID;					
        	//alert(JSON.stringify(outOfStockObj, null, 4));
        	TealeafWCJS.logStoreCustomEvent("WCStoreOutOfStock", outOfStockObj);
        },
        logStoreCustomEvent:function(eventName, customEventObj) {
        	if (typeof TLT != 'undefined') {
        		if(TLT.isInitialized()){
        			TLT.logCustomEvent(eventName, customEventObj);
        		}
        		else{				
        			setTimeout(function () { TealeafWCJS.logStoreCustomEvent(eventName, 
                               customEventObj)}, 100);
        		}
        			
        	}
        },
        createExplicitChangeEvent:function(id){
           if (typeof TLT != 'undefined') {
        		if (document.createEventObject && dojo.isIE < 9) { 				   
                          // for IE 
        			var evt=document.createEventObject();
        			dojo.byId(id).fireEvent("onchange",evt);
        		} else {
        			var evt=document.createEvent("HTMLEvents");
        			evt.initEvent("change", true, false);
        			dojo.byId(id).dispatchEvent(evt);	
        		} 
        	}		
        }
        }

    4. Save and close the file.

  3. Add a function to the StoreLocatorControllersDeclaration.js file.

    1. In the WebSphere Commerce development environment, go to the following directory:

    2. Open the StoreLocatorControllersDeclaration.js for editing.

    3. Add the following code to the wc.render.declareRefreshController()function for ID: Clarity.Confidence.Control (around line 200):

        dojo.query('div[id="checkOutOfStockRefresh"]').forEach(function(node, index, nodelist){
          dojo.eval(node.innerHTML);
         });

      The following code snippet is an example of how the added code looks within the file:

        wc.render.declareRefreshController({
            id: "selectedStoreListController",
            renderContext: wc.render.getContextById("selectedStoreListContext"),
            url: "",
            formId: ""
        
            ,modelChangedHandler: function(message, widget) {
                var controller = this;
                var renderContext = this.renderContext;
            }
            ,renderContextChangedHandler: function(message, widget) {
                var controller = this;
                var renderContext = this.renderContext;
                cursor_wait();
                widget.refresh(renderContext.properties);
            }
            ,postRefreshHandler: function(widget) {
                var controller = this;
                var renderContext = this.renderContext;
        
                var bopisTable = dojo.byId("bopis_table");
                if (bopisTable != null && bopisTable != "undefined") {
                     bopisTable.focus();
                }
                dojo.query('div[id="checkOutOfStockRefresh"]').forEach(function(n ode, index, nodelist){
                        dojo.eval(node.innerHTML);             });
                cursor_clear();
            }
        })

    4. Save and close the file.

The code that was added to the Aurora starter store files generates a JSON response when the out of stock event occurs. The JSON response is consumed by Tealeaf, allowing you to create events to track the cart abandonment scenario. The following is an example of the JSON response that is generated:


Creating the custom events in Tealeaf

  1. Log into the Tealeaf on Cloud interface.

  2. Click the Event Manager from the side menu.

  3. Create a Step Attribute to track the custom event.

    1. Click + New button, which is located in the upper menu, then click the Step attribute button.

    2. Configure the Step attribute with the following information:

      Setting Value
      Step attribute name Aurora custom event name
      Step pattern path .sessions[0].message.customEvent.name

    3. Click Save.

  4. Create a Step Attribute to track the physical store ID for the custom event.

    1. Click + New button, then click the Step attribute button.

    2. Configure the Step attribute with the following information:

      Setting Value
      Step attribute name Aurora custom event physical store ID
      Step pattern path “.sessions[0].message.customEvent.data.physicalStoreID”;

    3. Click Save.

  5. Create the Aurora out of stock event.

    1. Click the + New button, then click theEvent button.

    2. Configure the event with the following information:

      Setting Value
      Event name Aurora out of stock event
      Trigger Steps

    3. Click the + Condition button.

    4. Click Step attribute, find and click Aurora custom event name, then click the Selectbutton.

    5. Configure the Step attribute with the following values:

      First value | Equal: WCStoreOutofStock

    6. Click Save.

  6. Create the Aurora checkout started event. This event is used when you create the event for cart abandonment.

    1. Click the + New button, then click the Event button.

    2. Configure the event with the following information:

      Setting Value
      Event name Aurora checkout started event
      Trigger Steps

    3. Click the + Condition button.

    4. Click Session Attribute, find and click Last ScreenView of Session, then click the Select button.

    5. Configure the Session Attribute with the following values:

      Equal | /webapp/wcs/stores/servlet/CheckoutStoreSelectionView

    6. Click the + Condition button. For the condition type, click OR.

    7. Click Session Attribute, find and click Last ScreenView of Session, then click the Select button.

    8. Configure the Step attribute with the following values:

      Equal | /webapp/wcs/stores/servlet/OrderShippingBillingView

    9. Click the + Condition button. For the condition type, click OR.

    10. Click Session Attribute, find and click Last ScreenView of Session, then click the Select button.

    11. Configure the Session attribute with the following values:

      Equal | /webapp/wcs/stores/servlet/UnregisteredCheckoutView

      For Store metrics, select Track: First occurrence.

    12. Click Save.

  7. Create the Aurora checkout completed event. This event is used when you create the event for cart abandonment.

    1. Click the + New button, then click the Event button.

    2. Configure the event with the following information:

      Setting Value
      Event name Aurora checkout completed event
      Trigger Steps

    3. Click the + Condition button.

    4. Click Session attribute, find and click Last ScreenView of Session, then click Select.

    5. Configure the Step attribute with the following values:

      Equal | /webapp/wcs/stores/servlet/OrderShippingBillingConfirmationView

    6. Click the + Condition button. For the condition type, click AND.

    7. Click Event, find and click Aurora checkout started event, then click the Select button.

    8. Configure the Event with the following values:

      Exists in session | Is true

    9. Set Store metrics to Track: First occurrence.

    10. Click Save.

  8. Create the Aurora cart abandonment event.

    1. Click the + New button, then click the Event button.

    2. Configure the event with the following information:

      Setting Value
      Event name Aurora cart abandonment event
      Trigger Sessions
      Frequency At session end

    3. Click the + Condition button. For the condition type, click AND.

    4. Click Session attribute, find and click Aurora checkout started event, then click Select.

    5. Configure the Event with the following values:

      Exists in session | Is true

    6. Click the + Condition button. For the condition type, click AND.

    7. Click Event, , find and click Aurora checkout completed event, then click the Select button.

    8. Configure the Event with the following values:

      Exists in session | Is false

    9. Set Store metrics to Track: First occurrence.

    10. Click Save.

  9. Create event that associates cart abandonment with the out of stock customer event.

    1. Click the + New button, then click the Event button.

    2. Configure the event with the following information:

      Setting Value
      Event name Aurora cart abandonment with item out of stock
      Trigger Sessions
      Frequency At session end

    3. Click the + Condition button.

    4. Click Session attribute, find and click Aurora cart abandonment event, then click Select.

    5. Configure the Event with the following values:

      Exists in session | Is true

    6. Set Store metrics to Track: First occurrence


Results

With events in place, you see data that is collected for our Aurora store application in Tealeaf. Most events will be available in Tealeaf for reporting within 5 to 10 minutes after the source data is sent to Tealeaf. However, events that use the Trigger: Session End do not evaluate until the session times out. The timeout value is configurable to the customer, but timeout value is often set to 30 minutes by default. This means that the abandonment events do not appear until the user’s session times out.

Note: This demonstration is conducted on the WebSphere Commerce development environment. To implement this on the runtime environment, we must check your updated file into your source code repository, and follow the proper deployment guidelines.

This is one example of how we can customize events in Tealeaf to capture specific shopper scenarios on the store front. A limitation to this approach is that requires changes to your storefront, which would likely take place during a planned maintenance window. Tealeaf offers other ways in which we can customize events, specifically using raw data from session replays. By using session replay, we can extract raw data such as HTML requests to create custom events. For more information about this approach, see Tealeaf raw data.