Enhanced Ecommerce Bridge to JENTIS
  • 25 Apr 2023
  • 5 Minutes to read
  • Contributors
  • Dark
    Light

Enhanced Ecommerce Bridge to JENTIS

  • Dark
    Light

 

This article version is deprecated. Please find here the latest version: https://docs.jentis.com/docs/enhanced-ecommerce-bridge-to-jentis

Article Summary

The Google Tag Managers Data Layer is a very common schema to make data available on a website. And within that the Enhanced Ecommerce data model plays a big role on many shop systems. Thus a best practice is needed to translate that data and make it conveniently accessible to JENTIS Tag Manager. Here comes a short breakdown of the requirements, goals and steps how to implement it in JENTIS.

Best Practice

Before you start - beware!
Before considering translating an asynchronous and not directly connected data object you should always check if a direct integration is possible. Thus providing data via _jts.push API method reliably and consistently. With nothin to consider in terms of events out of sync. So this guide is really only relevant, if this best practice of direct communication is not feasible. Only in those scenarios a translation is the second best option.

Data is pushed asynchronously into the data queue “Data Layer”. Where the object itself is an array of event-objects. Every event is pushed to the dataLayer array and the initiators of that push can be any kind of application that are working asynchronously and often not orchestrated. Which is a good thing, as it enables multiple applications to work on the same objective in an abstract manner. However this asynchronous approach makes reading and organizing data in tracking a bit of a challenge.

Same applies when configuring a tracking with JENTIS. You have to have a concept on when to actually activate a tracking event, such as a “pageview”. Is it to be tracked at the moment the JENTIS JS snippet is initiated? Or do you wan’t to first wait for some ecommerce-data to be pushed to the dataLayer object?

That is really not as simple to answer. It always depends on the whole setup that you should overlook. With JENTIS there are two options to approach this scenario.

a) Create a JENTIS State when a certain event is pushed to the dataLayer

b) Use a JS Code snippet in JTM to activate the tracking via _jts API (ie. calling track:”submit” to activate the default “JENTIS Datalayer SEND”-State).

The option a) is covered in detail here: Data Layer Enhanced Ecommerce Bridge via JTM States

However this has one drawback: a new state created means, that you have to look into all individual default triggers and change those to your newly created state, if required.

To make life easier we can go with option b) – where we will just run a function and it will activate the default _jts API. Hence no triggers must be changed. Which we’d suggest is more convenient.

Detailed Description of Translation

This JS function creates a bridge from the global window.dataLayer object. Translating ecommerce data into the JENTIS data model (as defined in the reference: Ecommerce Tracking).

The following ecommerce actions will be translated accordingly from GTM enhanced ecommerce to JENTIS data model:

  • Product Impressions: product, productlist (productlist.name mapped to the first products list property)
  • Product Clicks: product, productlistclick (productlistclick.name mapped to actionField.list property)
  • Product Detail Impressions: product, productview
  • Add To Cart: product, addtocart
  • Remove From Cart: product, removefromcart
  • Promotion Impressions: Not Supported
  • Promotion Clicks: Not Supported
  • Checkout: product, checkout (checkout.step and checkout.option mapped to actionField step and option accordingly)
  • Purchase: product, order– mapping:
    • order.orderid: purchaes.actionField.id
    • order.brutto: purchase.actionField.revenue
    • order.shipping: purchase.actionField.shipping
  • Refunds: Not Supported

The following mapping applies on product level:

  • product.name: products.name
  • product.id: products.id
  • product.group: products.category
  • product.brutto: products.price
  • product.brand: products.brand
  • product.quantity: products.quantity or default to 1 if not set
  • product.position: products.position
  • product.variant: products.variant

Where the product is read from the according enhanced ecommerce events array.

Ecommerce Code Snippet

With the following code snippet you can activate a translation of enhanced ecommerce data schema to the JENTIS data model.

This guide is following the Google Tag Manager enhanced ecommerce integration documentation.

So the foundation is a proper use of this data schema. Only in those situations the translation will work out of the box!

To add a code snippet to a JTM container you must navigate to the “Components” – “Codes” section and click the “+”-icon to add a new element. For details of this process please see the according manual: Code Snippets

In the JavaScript Function text input field you can copy paste the following section.

//define events to listen to as single String values in array
//keep empty to listen to all events
var RELEVANT_EVENTS = ["ecommerce"];
var DISCOVERY_TYPE  = "parameter_key"; //either by event_name matching or existing parameter_key

var dl = window.dataLayer || [];
if(Array.isArray(dl) && dl.length && dl.length > 0) {
    dl.forEach(arrayItem => {
          if(DISCOVERY_TYPE == "event_name")
          initiateRelevantStates(arrayItem.event, arrayItem);
        else if(DISCOVERY_TYPE == "parameter_key")
          initiateRelevantStates(arrayItem[RELEVANT_EVENTS[0]] ? RELEVANT_EVENTS[0] : undefined, arrayItem);
    })
}

var originalPush = dl.push;
dl.push = function(args){
    originalPush.call(window["dataLayer"], args);
    if(DISCOVERY_TYPE == "event_name")
      initiateRelevantStates(args.event, args);
    else if(DISCOVERY_TYPE == "parameter_key")
      initiateRelevantStates(args[RELEVANT_EVENTS[0]] ? RELEVANT_EVENTS[0] : undefined, args);
}
window.dataLayer = dl;

function initiateRelevantStates(match_key, eec_object){
    if(Array.isArray(RELEVANT_EVENTS) && 
       RELEVANT_EVENTS.length > 0 &&
       typeof match_key != "undefined" &&
       typeof eec_object != "undefined"){
        if(RELEVANT_EVENTS.includes(match_key)){
        	try{
	            dl_eec_transform(eec_object);
	        } catch(e){console.log(e)}
        } else {
          }
      } else if(typeof event_name != "undefined" &&
              typeof eec_object != "undefined"){
        try{
	        dl_eec_transform(eec_object);
	    } catch(e){console.log(e)}
    }
}


//JTS DataLayer Ecommerce Transformer
function dl_eec_transform(dl_eec) {
    var jtsTrackObj = {};
    var jtsProds    = [];
    if(checkObject(dl_eec,["ecommerce","add"])){
        jtsTrackObj.track = "addtocart";
        jtsProds          = dl_eec.ecommerce.add.products;
    }
    else if(checkObject(dl_eec,["ecommerce","remove"])){
        jtsTrackObj.track = "removefromcart";
        jtsProds          = dl_eec.ecommerce.remove.products;
    }
    else if(checkObject(dl_eec,["ecommerce","detail"])){
        jtsTrackObj.track = "productview";
        jtsProds          = dl_eec.ecommerce.detail.products;
    }
    else if(checkObject(dl_eec,["ecommerce","impressions"])){
        jtsTrackObj.track = "productlist";
        jtsTrackObj.name  = "";
        jtsProds          = dl_eec.ecommerce.impressions;
        if(Array.isArray(dl_eec.ecommerce.impressions) && dl_eec.ecommerce.impressions[0].list)
            jtsTrackObj.name = dl_eec.ecommerce.impressions[0].list;
    }
    else if(checkObject(dl_eec,["ecommerce","click", "products"])){
        jtsTrackObj.track = "productlistclick";
        jtsTrackObj.name  = "";
        jtsProds          = dl_eec.ecommerce.click.products;
        if(dl_eec.ecommerce.click.actionField && dl_eec.ecommerce.click.actionField.list)
            jtsTrackObj.name = dl_eec.ecommerce.click.actionField.list;
    }
    else if(checkObject(dl_eec,["ecommerce","checkout","actionField"])){
        jtsTrackObj.track    = "checkout";
        jtsTrackObj.step     = dl_eec.ecommerce.checkout.actionField.step;
        jtsTrackObj.option   = dl_eec.ecommerce.checkout.actionField.option;
        jtsProds             = dl_eec.ecommerce.checkout.products;

    }
    else if(checkObject(dl_eec,["ecommerce","purchase","actionField"])){
        jtsTrackObj.track    = "order";
        jtsTrackObj.orderid  = dl_eec.ecommerce.purchase.actionField.id;
        jtsTrackObj.brutto   = dl_eec.ecommerce.purchase.actionField.revenue;
        jtsTrackObj.shipping = dl_eec.ecommerce.purchase.actionField.shipping;
        jtsProds             = dl_eec.ecommerce.purchase.products;

    }

    //transform products
    if(jtsProds && Array.isArray(jtsProds))
        jtsProds.forEach(function(product){
          var jts_product = {
                "track"        :   "product",
                "type"        :   jtsTrackObj.track,
                "id"        :   product.id,
                "name"      :   product.name,
                "group"     :   [product.category],
                "brutto"    :   product.price,
                "brand"     :   product.brand,
                "quantity"  :   product.quantity ? product.quantity : 1,
                "position"  :   product.position,
                "variant"   :   product.variant
            };
          for(var i = 1; i <= 200; i++)
            if(product["dimension"+i])
              jts_product["dimension"+i] = product["dimension"+i];
          
          _jts.push(jts_product);
        })

    _jts.push(jtsTrackObj);
  
      _jts.push({"track":"submit"})
}

function checkObject(object,aSplit,counter) {
    if(typeof counter === "undefined") {
        counter = 0;
    }

    if(aSplit.length === counter) {
        return object;
    } else {
        if(typeof object[aSplit[counter]] !== "undefined") {
            return checkObject(object[aSplit[counter]],aSplit,++counter);
        } else {
            return null;
        }
    }
}



Was this article helpful?