Appendix E: Understand the Add to Cart BML – Customized Integrations and Multi-Site Set Up (19D and Later)

Users with customized integrations and multi-site set ups (19D and later) who have previously customized their Add to Cart BML need to modify and update their BML.

The following provides the Add to Cart BML for Customized Integrations and Multi-Site Set Up 19D and later:

// Initialize variables
MODEL_PATH = "/configuration/configureResponse/item/model";
CONFIG_ID_PATH = "/configuration/configureResponse/item/@configurationId";
CURRENCY_CODE_PATH = "/configuration/configureResponse/attributes/attribute[@_variableName='currencyCode']/value";
TOTAL_PRICE_PATH = "/configuration/configureResponse/price/totalPrice";
SPARE_PART_PATH = "/configuration/configureResponse/spare/rule/item/part";
SPARE_QUANTITY_PATH = "/configuration/configureResponse/spare/rule/item/quantity";
SPARE_PRICE_PATH = "/configuration/configureResponse/spare/rule/item/price";
SPARE_SELECTED_PATH = "/configuration/configureResponse/spare/rule/item/selected";
BOM_ITEM_PATH = "/configuration/configureResponse/bomItem";
BOM_PRICE_PATH = "/configuration/configureResponse/price/bomPrice";
 
CART_TEMPLATE_LOCATION = "$BASE_PATH$/CommerceCloud/AddToCartPayload-Cloud.txt";
SPARE_TEMPLATE_LOCATION = "$BASE_PATH$/CommerceCloud/Recommended_Items_Payload-Cloud.txt";
 
payload = "";
sparesList = "";
priceTotal = 0.0;
baseModelPrice = 0.0;
sparePart = String[1];
spareQuantity = String[1];
sparePrice = String[1];
spareSelected = String[1];
singleSpareDict = dict("string");
configDict = dict("string");
 
// Create array of XML paths:
pathArray = string[];
sparePathArray = string[];
 
// For Model/Price Properties
append(pathArray, MODEL_PATH);
append(pathArray, CONFIG_ID_PATH);
append(pathArray, CURRENCY_CODE_PATH);
append(pathArray, TOTAL_PRICE_PATH);
 
// For BOM Item Property
append(pathArray, BOM_ITEM_PATH);
append(pathArray, BOM_PRICE_PATH);
 
// For Rec Item Properties (needs its own array)
append(sparePathArray, SPARE_PART_PATH);
append(sparePathArray, SPARE_QUANTITY_PATH);
append(sparePathArray, SPARE_PRICE_PATH);
append(sparePathArray, SPARE_SELECTED_PATH);
 
// Extract data from configXML
pathDict = readxmlsingle(configXML, pathArray);
spareDict = readxmlmultiple(configXML, sparePathArray);
 
model = get(pathDict, MODEL_PATH);
configId = get(pathDict, CONFIG_ID_PATH);
currency = get(pathDict, CURRENCY_CODE_PATH);
totalPrice = get(pathDict, TOTAL_PRICE_PATH);
bomPrice = get(pathDict, BOM_PRICE_PATH);
bomItem = get(pathDict, BOM_ITEM_PATH);
 
// Convert totalPrice (which is a misleading name) to numeric value, set as baseModelPrice
totalPrice = replace(totalPrice, ",", "");
if (isnumber(substring(totalPrice, 1))) {
    totalPriceNum = getcurrencyvalue(totalPrice, currency);
    priceTotal = priceTotal + totalPriceNum;
}
baseModelPrice = priceTotal;
 
// Add BOM total price to priceTotal (which is the REAL total price), with the same conversion as the base price
if (NOT(isnull(bomPrice))) {
    bomPrice = replace(bomPrice, ",", "");
    if (isnumber(substring(bomPrice, 1))) {
        bomPriceNum = getcurrencyvalue(bomPrice, currency);
        priceTotal = bomPriceNum + priceTotal;
    }
}
 
// Get Recommended Items
for sparepath in sparePathArray {
    if (find(sparepath, "part") <> -1) {
        sparePart = get(spareDict, sparepath);
    }
    elif(find(sparepath, "quantity") <> -1) {
        spareQuantity = get(spareDict, sparepath);
    }
    elif(find(sparepath, "price") <> -1) {
        sparePrice = get(spareDict, sparepath);
    }
    elif(find(sparepath, "selected") <> -1) {
        spareSelected = get(spareDict, sparepath);
    }
}
 
// Format Rec Items payload
if (isnull(sparePart)) {
    print "No Recommended Items";
} else {
    spareListSize = sizeofarray(sparePart);
    spareArray = integer[spareListSize];
     
    i = 0;
    for eachSpare in spareArray {
        if (spareSelected[i] == "true") {
            //Convert price, similar to Base and BOM prices above
            priceString = substring(sparePrice[i], 1);
            priceString = replace(priceString, ",", "");
            if (isnumber(priceString)) {
                sparePrice[i] = string(getcurrencyvalue(priceString, currency));
                priceTotal = priceTotal + atof(sparePrice[i]);
            } else {
                sparePrice[i] = "0";
            }
             
            // Add basic part fields to dictionary from array dictionary
            put(singleSpareDict, "part", sparePart[i]);
            put(singleSpareDict, "quantity", spareQuantity[i]);
            put(singleSpareDict, "price", sparePrice[i]);
             
            // Generate template and set values from dictionary
            singleSparePayload = applytemplate(SPARE_TEMPLATE_LOCATION, singleSpareDict);
             
            // Get Recurring Charge fields
            part_num = sparePart[i];
            partCustomFieldsDict = bmql("SELECT part_number, custom_field5, custom_field4, custom_field6, custom_field8 FROM _parts WHERE part_number = $part_num");
             
            for each in partCustomFieldsDict {
                if (get(each, "custom_field8") == "Recurring") {
                    singleSparePayload = replace(singleSparePayload, "{{pricePeriod}}", get(each, "custom_field4"));
                    singleSparePayload = replace(singleSparePayload, "{{recurringPrice}}", get(each, "custom_field5"));
                    singleSparePayload = replace(singleSparePayload, "{{duration}}", get(each, "custom_field6"));
                } else {
                    childPayloadJson = json(singleSparePayload);
                    jsonremove(childPayloadJson, "recurringCharge");
                    singleSparePayload = jsontostr(childPayloadJson);
                }
            }
             
            // Add Item to List
            if (sparesList == "") {
                sparesList = singleSparePayload;
            } else {
                sparesList = sparesList + "," + singleSparePayload;
            }
        }   
        i = i + 1;
    }
}
 
// Get the BOM Items
if (isnull(bomItem)) {
    print "No BOM Items";
    bomItem = "";
} else {
    // Get part numbers for each BOM item, convert to string array for bmql
    bomJson = json(bomItem);
 
    // Remove extraneous BOM fields (may have to revert if CC was expecting to use them)
    jsonpathremove(bomJson, "$..variableName");
    jsonpathremove(bomJson, "$..definition");
    jsonpathremove(bomJson, "$..category");
 
    // Replacing all 0 prices with actual number 0
    bomPriceArray = jsonpathgetmultiple(bomJson, "$.._price_unit_price_each");
    replace_lookup = boolean[];
    bomPricesString = jsonarraytostr(bomPriceArray);
    bomPricesString = replace(replace(replace(bomPricesString, "\"", ""), "[", ""), "]", "");
    bomPricesStringArray = split(bomPricesString, ",");
 
    i = 0;
    for each in bomPricesStringArray {
        append(replace_lookup, isnumber(each));
        i = i + 1;
    }
 
    i = 0;
    for each in replace_lookup {
        if (i == 0 and each == false) {
            jsonpathset(bomJson, "$.fields._price_unit_price_each", "0");
        }
        elif(each == false) {
            str = "$.children[" + string(i - 1) + "].fields._price_unit_price_each";
            jsonpathset(bomJson, str, "0");
        }
 
        i = i + 1;
    }
 
    bomItem = jsontostr(bomJson);
    bomPartsArray = jsonpathgetmultiple(bomJson, "$..partNumber");
    bomPartsString = jsonarraytostr(bomPartsArray);
    bomPartsString = replace(replace(replace(bomPartsString, "\"", ""), "[", ""), "]", "");
    bomPartsStringArray = split(bomPartsString, ",");
    bomParts = bmql("SELECT part_number, custom_field5, custom_field4, custom_field6, custom_field8 FROM _parts WHERE part_number IN $bomPartsStringArray");
 
    // Get path for each part, add recurringCharge to them all
    for each in bomParts {
        partField = "\"partNumber\":\"" + get(each, "part_number") + "\",";
        recurringTemplate = "\"recurringCharge\":{ \"amount\":,\"frequency\":,\"duration\":},";
 
        if (get(each, "custom_field8") == "Recurring") {
            recurringTemplate = replace(recurringTemplate, "frequency\":", "frequency\":\"" + get(each, "custom_field4") + "\"");
            recurringTemplate = replace(recurringTemplate, "amount\":", "amount\":\"" + get(each, "custom_field5") + "\"");
            recurringTemplate = replace(recurringTemplate, "duration\":", "duration\":\"" + get(each, "custom_field6") + "\"");
        } else {
            recurringTemplate = "";
        }
        bomItem = replace(bomItem, partField, partField + recurringTemplate);
    }
 
    // Handle 0 prices in configuration (this may only fix English users)
    bomItem = replace(bomItem, "\"partNumber\":", "\"catalogRefId\":");
    bomItem = replace(bomItem, "On Request", "0"); 
     
    // Unflatten
    bomJson = convertbomtohier(json(bomItem));
    bomItem = jsontostr(bomJson);
}
 
// Format main template with subcomponents and properties
put(configDict, "commerceItemId", "");
put(configDict, "model", model);
put(configDict, "ConfigId", configId);
put(configDict, "currency", currency);
put(configDict, "totalPrice", string(priceTotal));
put(configDict, "basePrice", string(baseModelPrice));
put(configDict, "ChildItems", sparesList);
put(configDict, "BomItems", bomItem);
payload = applytemplate(CART_TEMPLATE_LOCATION, configDict);
payload = replace(payload, "&quot;", "\""); // encoding bug on applytemplate
 
return payload;