The client would like to sync orders with a time-out error. We identify orders with time-out errors using a custom field in the item fulfillment record.
/**
* @NApiVersion 2.1
* @NScriptType MapReduceScript
*/
/*************************************************************************************
***********
*
* XSEED Education Pte Ltd-IND-NS
*
* XSEED-534: E-shipz integration
*
*
*************************************************************************************
***********
*
* Author: Jobin and Jismi IT Services LLP
*
* Date Created : 15-August-2023
*
* Description: This script is used to integrate order details to Eshipz portal and Output to be pushed back to Netsuite Item fulfillment(Orders in which time out error occured).
*
* REVISION HISTORY
*
* @version 1.0 XSEED-534: 15-August-2023: Created the initial build by JJ0177
*
*
*************************************************************************************
**********/
define([‘N/search’, ‘N/email’, ‘N/render’, ‘N/https’, ‘N/url’, ‘N/format’, ‘N/record’],
(search, email, render, https, url, format, record) => {
/**
* Function to check whether the field has an empty value or not.
* @param {parameter} parameter – fieldValue
* @returns {boolean} true – if the value is not empty
* @returns {boolean} false – if the value is empty
*/
function checkForParameter(parameter) {
try {
if (parameter != “” && parameter != null && parameter != undefined && parameter != “null” && parameter != “undefined” && parameter != ” “ && parameter != false && parameter != ” && parameter != ‘ ‘) {
return true;
} else {
return false;
}
}
catch (e) {
log.debug({
title: “Error @ empty check Function: “, details: e.name + ‘ : ‘ + e.message
});
}
}
/**
* Function to fetch the carton internal id
* @param {number} id -internal id of item fulfillment record
* @returns {object} object containing carton id
*/
function fetchCartonId(id) {
try {
let itemfulfillmentSearchObj = search.create({
type: “itemfulfillment”,
filters:
[
[“type”, “anyof”, “ItemShip”],
“AND”,
[“internalid”, “anyof”, id],
“AND”,
[“cogs”, “is”, “F”],
“AND”,
[“shipping”, “is”, “F”],
],
columns:
[
search.createColumn({
name: “internalid”,
join: “CUSTRECORD_IF_CARTON_LINK”,
label: “Internal ID”
})
]
});
let searchResultCount = itemfulfillmentSearchObj.runPaged().count;
let cartonIdSpecificObj = [];
if (searchResultCount > 0) {
itemfulfillmentSearchObj.run().each(function (result) {
let cartonsId = result.getValue({
name: “internalid”,
join: “CUSTRECORD_IF_CARTON_LINK”,
label: “Internal ID”
});
cartonIdSpecificObj.push(cartonsId)
return true;
});
}
return cartonIdSpecificObj;
} catch (error) {
log.error(“error@fetchCartonId”, error)
}
}
/**
* Function to fetch the carton internal id
* @param {object} responseBody -response body
* @param {object} ifRec -item fulfillment record
* @returns {object} object containing carton id
*/
function processResponse(responseBody, ifRec) {
try {
let url = “https://track.eshipz.com/track?awb=”;
let parentAwb = responseBody[0].label_meta.awb
let trackingUrl = url + parentAwb
let slugDetail = responseBody[0].slug;
let labelUrl = responseBody[0].label_meta.url;
let childwaybill = responseBody[0].label_meta.package_numbers
record.submitFields({
type: record.Type.ITEM_FULFILLMENT,
id: ifRec.id,
values: {
custbody_if_parent_waybill_no: parentAwb,
custbody_courier_detail: slugDetail,
custbody_tracking_website: trackingUrl,
custbody_jj_is_synced_xseed534: true,
custbody_jj_master_label: labelUrl,
shipstatus: “C”,
custbody_integration_error:”
},
options: {
enableSourcing: false,
ignoreMandatoryFields: true
}
});
if (checkForParameter(childwaybill)) {
let ifId=ifRec.id
let cartonId = fetchCartonId(ifId)
log.debug(“cartonId”,cartonId)
let remainingPackageSeries = [];
if (checkForParameter(cartonId)) {
for (let i = 0; i < childwaybill.length; i++) {
if (i < cartonId.length) {
// const id = cartonId[i];
const id = cartonId[i % cartonId.length];
log.debug(“id”,id)
let otherId = record.submitFields({
type: ‘customrecord_carton_details’,
id: id,
values: {
‘custrecord_if_child_waybill_no’: childwaybill[i]
}
});
} else {
remainingPackageSeries.push(childwaybill[i]);
}
}
}
else {
remainingPackageSeries = childwaybill;
}
log.debug(“remainingPackageSeries”,remainingPackageSeries)
let newArray = remainingPackageSeries.join(‘,’);
log.debug(“newArray”,newArray)
record.submitFields({
type: record.Type.ITEM_FULFILLMENT,
id: ifRec.id,
values: {
‘custbody_jj_package_series’: newArray,
},
options: {
enableSourcing: false,
ignoreMandatoryFields: true
}
});
}
} catch (error) {
log.debug(“error@processResponse”, error)
}
}
/**
* Defines the function that is executed at the beginning of the map/reduce process and generates the input data.
* @param {Object} inputContext
* @param {boolean} inputContext.isRestarted – Indicates whether the current invocation of this function is the first
* invocation (if true, the current invocation is not the first invocation and this function has been restarted)
* @param {Object} inputContext.ObjectRef – Object that references the input data
* @typedef {Object} ObjectRef
* @property {string|number} ObjectRef.id – Internal ID of the record instance that contains the input data
* @property {string} ObjectRef.type – Type of the record instance that contains the input data
* @returns {Array|Object|Search|ObjectRef|File|Query} The input data to use in the map/reduce process
* @since 2015.2
*/
const getInputData = (inputContext) => {
try {
let timeOutSpecificArray = [];
var itemfulfillmentSearchObj = search.create({
type: “itemfulfillment”,
filters:
[
[“type”, “anyof”, “ItemShip”],
“AND”,
[“custbody_jj_is_timeout_occured”, “is”, “T”],
“AND”,
[“mainline”, “is”, “T”],
“AND”,
[“custbody_jj_is_synced_xseed534”, “is”, “F”]
],
columns:
[
search.createColumn({ name: “tranid”, label: “Document Number” }),
search.createColumn({ name: “internalid”, label: “Internal ID” }),
search.createColumn({name: “custbody_jj_ezhipz_ref_num”, label: “E-shipz Reference Number”})
]
});
let searchResultCount = itemfulfillmentSearchObj.runPaged().count;
log.debug(“itemfulfillmentSearchObj result count”, searchResultCount);
itemfulfillmentSearchObj.run().each(function (result) {
let timeOutSpecificObj = {};
timeOutSpecificObj.timeOutSpecificObjDocNum = result.getValue({ name: “tranid”, label: “Document Number” });
timeOutSpecificObj.timeOutSpecificObjInternalId = result.getValue({ name: “internalid”, label: “Internal ID” });
timeOutSpecificObj.timeOutRefNum=result.getValue({name: “custbody_jj_ezhipz_ref_num”, label: “E-shipz Reference Number”})
timeOutSpecificArray.push(timeOutSpecificObj);
return true;
});
return timeOutSpecificArray;
} catch (error) {
log.debug(“error @getInput”, error);
return [];
}
}
/**
* Defines the function that is executed when the reduce entry point is triggered. This entry point is triggered
* automatically when the associated map stage is complete. This function is applied to each group in the provided context.
* @param {Object} reduceContext – Data collection containing the groups to process in the reduce stage. This parameter is
* provided automatically based on the results of the map stage.
* @param {Iterator} reduceContext.errors – Serialized errors that were thrown during previous attempts to execute the
* reduce function on the current group
* @param {number} reduceContext.executionNo – Number of times the reduce function has been executed on the current group
* @param {boolean} reduceContext.isRestarted – Indicates whether the current invocation of this function is the first
* invocation (if true, the current invocation is not the first invocation and this function has been restarted)
* @param {string} reduceContext.key – Key to be processed during the reduce stage
* @param {List<String>} reduceContext.values – All values associated with a unique key that was passed to the reduce stage
* or processing
* @since 2015.2
*/
const reduce = (reduceContext) => {
try {
let dataObj = JSON.parse(reduceContext.values);
let ifDocNum = dataObj.timeOutSpecificObjDocNum;
let ifInternalId = dataObj.timeOutSpecificObjInternalId;
let ifRefNum=dataObj.timeOutRefNum;
log.debug(“ifDocNum”,ifDocNum)
let getUrl = ‘https://app.eshipz.com/proconnect/api/v1/get-shipments?db_filters={“customer_referenc”:”‘ + ifRefNum + ‘”}’;
let accessHeaders = {
‘X-API-TOKEN’: ‘6362545a0afce014f8be7549’,
‘Content-Type’: ‘application/json’,
};
let responses = https.get({
url: getUrl,
headers: accessHeaders,
});
log.debug(“responses”,responses)
if (responses.code == 200) {
let responseBody = JSON.parse(responses.body)
log.debug(“responseBody.length”,responseBody.length)
let ifrecord = record.load({
type: record.Type.ITEM_FULFILLMENT,
id: ifInternalId,
isDynamic: true,
});
if(responseBody.length==0){
record.submitFields({
type: record.Type.ITEM_FULFILLMENT,
id: ifrecord.id,
values: {
‘custbody_integration_error’: “Time out error occured due to large quantity is trying to sync with E-shipz”,
‘shipstatus’: “B”
},
options: {
enableSourcing: false,
ignoreMandatoryFields: true
}
});
}
else if (checkForParameter(responseBody)) {
processResponse(responseBody, ifrecord);
}
}
else
if (responseBody.code != 200) {
let bodyObject = JSON.parse(responses.body);
let detailsArray = bodyObject.meta.details;
record.submitFields({
type: record.Type.ITEM_FULFILLMENT,
id: ifRec.id,
values: {
‘custbody_integration_error’: detailsArray[2],
‘shipstatus’: “B”
},
options: {
enableSourcing: false,
ignoreMandatoryFields: true
}
});
}
} catch (error) {
log.error(“error @reduce”, error);
}
}
return { getInputData, reduce }
});