Jira Code: OSI-89 Fedex
The script sync shipment status for UPS, USPS, Fedex in ItemFulfillment using API so as to find Aging/Inactive shipments. We are fetching the tracking details using API and determining whether the shipment has been started or not. If it is not yet started we can conclude that the shipment is inactive and we can update that correspondingly in NetSuite.
/**
* @NApiVersion 2.x
* @NScriptType ScheduledScript
* @NModuleScope SameAccount
*/
/************************************************************************************************
* * OneSource IML | OSI - 89 | Fedex Tracking STATUS | Aging FedEx Shipments *
* **********************************************************************************************
*
* Author: Jobin & Jismi IT Services LLP
*
* Date Created : 13-May-2019
*
***********************************************************************************************/
define(['N/xml', 'N/url', 'N/email', 'N/https', 'N/record', 'N/runtime', 'N/search', 'N/task', 'N/config'],
/**
* @param {xml} xml
* @param {url} url
* @param {email} email
* @param {https} https
* @param {record} record
* @param {runtime} runtime
* @param {search} search
* @param {task} task
* @param {config} config
*/
function(xml, url, email, https, record, runtime, search, task, config) {
"use strict";
var fetchTrackingInfo = null,
trackingStatus = null,
agingItemFulFillments = [],
noTrackingInformation = [];
//To check whether a value exists in parameter
function checkForParameter(parameter, parameterName) {
if (parameter !== null && parameter !== undefined && parameter !== false && parameter !== "null" && parameter !== "undefined" && parameter !== "false" && parameter !== "" && parameter !== " ")
return true;
if (parameterName)
log.debug("Empty Value found", "Empty Value for parameter " + parameterName);
return false;
}
//To assign a default value if the it is empty
function assignDefaultValue(value, defaultValue) {
if (checkForParameter(value)) return value;
return defaultValue;
}
//Common Try-Catch function
function applyTryCatch(DATA_OBJ, NAME) {
function tryCatch(myfunction, key) {
return function() {
try {
return myfunction.apply(this, arguments);
} catch (e) {
log.error("error in " + key, e);
return false;
}
};
}
for (var key in DATA_OBJ) {
if (typeof DATA_OBJ[key] === "function") {
DATA_OBJ[key] = tryCatch(DATA_OBJ[key], NAME + "." + key);
}
}
}
//Encapsulates Scheduler Functions
var scheduler = {
currentScript: undefined,
run: function(key) {
return ({
initialise: function() {
return scheduler.initialise();
},
id: function() {
return scheduler.getScriptId();
},
deploymentId: function() {
return scheduler.getDeploymentId();
},
remainingUsage: function() {
return scheduler.getRemainingUsage();
},
parameter: function(args) {
return scheduler.getParameter(args);
},
reschedule: function(args) {
return scheduler.submitTask(args);
}
})[key](arguments);
},
initialise: function() {
this.currentScript = runtime.getCurrentScript();
},
getScriptId: function() {
return this.currentScript.id;
},
getDeploymentId: function() {
return this.currentScript.deploymentId;
},
getRemainingUsage: function() {
return Number(runtime.getCurrentScript().getRemainingUsage());
},
getParameter: function(args) {
if (args[1])
return assignDefaultValue(this.currentScript.getParameter({
name: args[1].toString().toLowerCase().trim()
}), false);
return false;
},
submitTask: function(args) {
return task.create({
taskType: task.TaskType.SCHEDULED_SCRIPT,
scriptId: assignDefaultValue(args[1] ? args[1] : this.run("id"), this.run("id")),
deploymentId: assignDefaultValue(args[2] ? args[2] : this.run("deploymentId"), this.run("deploymentId")),
params: (function() {
var tempObj = {};
if (args[3])
for (var key in args[3])
tempObj[key] = args[3][key];
return tempObj;
})()
}).submit();
}
};
applyTryCatch(scheduler, "scheduler");
//FedEx REST API Call
var FedExAPIObj = {
getCredentials: function() {
return {
'URL': ''//'https://ws.fedex.com:443/web-services',
'Key': ''//'pJUyd7QEbdrihqVj',
'Password': ''//'aciIkFq8jSbIyRUFWHuJfKFFf',
'ShippingAccountNumber': ''//'361846400',
'WebServicesMeterNumber': ''//'113414546'
};
},
fetchTrackingInfo: function(TRACKING_NUMBER, ShipmentAccountNumber, ShipDateRangeBegin, ShipDateRangeEnd) {
var authObj = FedExAPIObj.getCredentials();
var XML_BODY;
if (checkForParameter(ShipDateRangeBegin) && checkForParameter(ShipDateRangeEnd))
XML_BODY = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v16="http://fedex.com/ws/track/v16"> <soapenv:Header /> <soapenv:Body> <TrackRequest xmlns="http://fedex.com/ws/track/v3"> <WebAuthenticationDetail> <UserCredential> <Key>' + authObj.Key + '</Key> <Password>' + authObj.Password + '</Password> </UserCredential> </WebAuthenticationDetail> <ClientDetail> <AccountNumber>' + authObj.ShippingAccountNumber + '</AccountNumber> <MeterNumber>' + authObj.WebServicesMeterNumber + '</MeterNumber> </ClientDetail> <TransactionDetail> <CustomerTransactionId>ActiveShipping</CustomerTransactionId> </TransactionDetail> <Version> <ServiceId>trck</ServiceId> <Major>3</Major> <Intermediate>0</Intermediate> <Minor>0</Minor> </Version> <PackageIdentifier> <Value>' + TRACKING_NUMBER + '</Value> <Type>TRACKING_NUMBER_OR_DOORTAG</Type> </PackageIdentifier> <ShipDateRangeBegin>' + ShipDateRangeBegin + '</ShipDateRangeBegin> <ShipDateRangeEnd>' + ShipDateRangeEnd + '</ShipDateRangeEnd> <ShipmentAccountNumber>' + ShipmentAccountNumber + '</ShipmentAccountNumber> <IncludeDetailedScans>1</IncludeDetailedScans> </TrackRequest> </soapenv:Body> </soapenv:Envelope>';
else
XML_BODY = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v16="http://fedex.com/ws/track/v16"> <soapenv:Header /> <soapenv:Body> <TrackRequest xmlns="http://fedex.com/ws/track/v3"> <WebAuthenticationDetail> <UserCredential> <Key>' + authObj.Key + '</Key> <Password>' + authObj.Password + '</Password> </UserCredential> </WebAuthenticationDetail> <ClientDetail> <AccountNumber>' + authObj.ShippingAccountNumber + '</AccountNumber> <MeterNumber>' + authObj.WebServicesMeterNumber + '</MeterNumber> </ClientDetail> <TransactionDetail> <CustomerTransactionId>ActiveShipping</CustomerTransactionId> </TransactionDetail> <Version> <ServiceId>trck</ServiceId> <Major>3</Major> <Intermediate>0</Intermediate> <Minor>0</Minor> </Version> <PackageIdentifier> <Value>' + TRACKING_NUMBER + '</Value> <Type>TRACKING_NUMBER_OR_DOORTAG</Type> </PackageIdentifier> <ShipmentAccountNumber>' + ShipmentAccountNumber + '</ShipmentAccountNumber> <IncludeDetailedScans>1</IncludeDetailedScans> </TrackRequest> </soapenv:Body> </soapenv:Envelope>';
var response = https.post({
url: authObj.URL,
headers: {
'content-type': 'application/xml',
'accept': 'application/xml'
},
body: XML_BODY
});
if (response.code == 200)
return response.body;
else
return false;
}
};
applyTryCatch(FedExAPIObj, "FedExAPIObj");
//ups REST API Call
var USPSAPIObj = {
getCredentialsUsps: function(TRACKING_NUMBER) {
return {
'URL': 'https://secure.shippingapis.com/ShippingAPI.dll?API=TrackV2&XML=<TrackRequest USERID="443ONESO6301"><TrackID ID="' + TRACKING_NUMBER + '"></TrackID></TrackRequest>'
};
},
fetchTrackingInfoUsps: function(TRACKING_NUMBER) {
var authObj = USPSAPIObj.getCredentialsUsps(TRACKING_NUMBER);
var XML_BODY;
var response = https.get({
url: authObj.URL,
headers: {
'content-type': 'application/xml',
'accept': 'application/xml'
}
});
if (response.code == 200)
return response.body;
else
return false;
}
};
applyTryCatch(USPSAPIObj, "USPSAPIObj");
//FedEx REST API Call
var UPSAPIObj = {
getCredentialsUps: function() {
return {
'URL': ''//'https://onlinetools.ups.com/ups.app/xml/Track'
};
},
fetchTrackingInfoUps: function(TRACKING_NUMBER, ShipmentAccountNumber) {
var authObj = USPSAPIObj.getCredentialsUps();
var XML_BODY;
if (TRACKING_NUMBER) {
XML_BODY = '<?xml version="1.0"?> <AccessRequest xml:lang="en-US"> <AccessLicenseNumber>1D63183B54F001C8</AccessLicenseNumber> <UserId>jcromanski</UserId> <Password>yamishmish</Password> </AccessRequest> <?xml version="1.0"?> <TrackRequest xml:lang="en-US"> <Request> <RequestAction>Track</RequestAction> <RequestOption>activity</RequestOption> </Request> <TrackingNumber>' + TRACKING_NUMBER + '</TrackingNumber> </TrackRequest>'
}
var response = https.post({
url: authObj.URL,
headers: {
'content-type': 'application/xml',
'accept': 'application/xml'
},
body: XML_BODY
});
if (response.code == 200)
return response.body;
else
return false;
}
};
applyTryCatch(UPSAPIObj, "UPSAPIObj");
//dataSets from Saved Search and formating Saved Search results
var dataSets = {
fetchSavedSearchColumn: function(savedSearchObj, priorityKey) {
var columns = savedSearchObj.columns;
var columnsData = {},
columnName = '';
columns.forEach(function(result, counter) {
columnName = '';
if (result[priorityKey]) {
columnName += result[priorityKey];
} else {
if (result.summary)
columnName += result.summary + '.';
if (result.formula)
columnName += result.formula + '.';
if (result.join)
columnName += result.join + '.';
columnName += result.name;
}
columnsData[columnName] = result;
});
return columnsData;
},
formatSingleResult: function(searchResult, columns) {
var responseObj = {};
for (var column in columns)
responseObj[column] = assignDefaultValue(searchResult.getValue(columns[column]), "");
return responseObj;
},
feDexItemFulfillment: function() {
var fetchIF = search.load({
id: 'customsearch_osi82_aging_fedex_shipments' //OSI-82 Aging FedEx Shipments - DO NOT DELETE
});
return {
columns: dataSets.fetchSavedSearchColumn(fetchIF, 'label'),
searchObj: fetchIF
};
},
upsItemFulfillment: function() {
var fetchIF = search.load({
id: 'customsearch_osi89_aging_ups_shipments_2' //OSI-82 Aging ups Shipments - DO NOT DELETE
});
return {
columns: dataSets.fetchSavedSearchColumn(fetchIF, 'label'),
searchObj: fetchIF
};
},
uspsItemFulfillment: function() {
var fetchIF = search.load({
id: 'customsearch_osi89_aging_usps_shipment_2' //OSI-82 Aging usps Shipments - DO NOT DELETE
});
return {
columns: dataSets.fetchSavedSearchColumn(fetchIF, 'label'),
searchObj: fetchIF
};
},
};
applyTryCatch(dataSets, "dataSets");
//Main or Root Function of Scheduled Script
var main = {
isSandBox: function() {
var companyInfo = config.load({
type: config.Type.COMPANY_INFORMATION
});
var ns_companyid = companyInfo.getValue({
fieldId: 'companyid'
});
ns_companyid = ns_companyid.toString().trim().toLowerCase();
if (ns_companyid.indexOf('sb') > -1)
return true;
return false;
},
hasEnoughUsage: function() {
if (scheduler.run("remainingUsage") < 150) { //Rescheduling Condition
scheduler.run("reschedule");
log.debug('*** RESCHEDULE ***', '*** SCHEDULED SCRIPT RESCHEDULES ***');
return false;
}
return true;
},
NS_XML_PARSER: function(XML_DATA, XML_TAG) {
var xmlDocument = xml.Parser.fromString({
text: XML_DATA
});
var xmlParseArray = xmlDocument.getElementsByTagName({
tagName: XML_TAG
});
return xmlParseArray;
},
generateReferenceToRecord: function(INTERNAL_ID, TRAN_ID) {
if (!INTERNAL_ID || !TRAN_ID)
return null;
return '<strong><a href="' + url.resolveRecord({
recordType: record.Type.ITEM_FULFILLMENT,
recordId: INTERNAL_ID,
isEditMode: false
}) + '">#' + TRAN_ID + '</a></strong>';
},
fedexDetails: function(scriptContext) {
//Fetch and Item Fulfillment
var itemFulfillmentSearch = dataSets.feDexItemFulfillment();
log.debug('itemFulfillmentSearch', itemFulfillmentSearch);
itemFulfillmentSearch.searchObj.run().each(function(eachResult) {
if (main.hasEnoughUsage()) {
try {
fetchTrackingInfo = trackingStatus = null;
eachResult = dataSets.formatSingleResult(eachResult, itemFulfillmentSearch.columns);
log.debug('eachResult itemFulfillment', eachResult);
//Request FedEx for Tracking Information
fetchTrackingInfo = FedExAPIObj.fetchTrackingInfo((eachResult.TrackingNumber).toString().trim().split(" ")[0], eachResult.ShipmentAccountNumber);
log.debug('eachResult fetchTrackingInfo', fetchTrackingInfo);
//Fetch 'StausCode' from FedEx Response
if (fetchTrackingInfo)
trackingStatus = main.NS_XML_PARSER(fetchTrackingInfo, 'StatusCode');
log.debug('trackingStatus', trackingStatus);
if (checkForParameter(trackingStatus)) //If there is 'StausCode', update itemFulfillment record
if (checkForParameter(trackingStatus[0]))
if (checkForParameter(trackingStatus[0].textContent)) {
if (trackingStatus[trackingStatus.length - 1].textContent == 'OC' || (trackingStatus[trackingStatus.length - 1].textContent).toString().trim().toLowerCase() == 'oc') //If there is aging FedEx Shipments
agingItemFulFillments.push(main.generateReferenceToRecord(eachResult.internalid, eachResult.PackageIdentifier));
record.submitFields({
type: record.Type.ITEM_FULFILLMENT,
id: eachResult.internalid,
values: {
custbody_tracking_status: (trackingStatus[trackingStatus.length - 1].textContent).toString().trim() //Tracking Status
},
options: {
enableSourcing: false,
ignoreMandatoryFields: true
}
});
return true;
}
} catch (erIF) {
log.debug('erIF', erIF);
}
noTrackingInformation.push(main.generateReferenceToRecord(eachResult.internalid, eachResult.PackageIdentifier));
return true;
}
return false;
});
},
upsDetails: function(scriptContext) {
//Fetch and Item Fulfillment
var upsitemFulfillmentSearch = dataSets.upsItemFulfillment();
log.debug('upsitemFulfillmentSearch', upsitemFulfillmentSearch);
upsitemFulfillmentSearch.searchObj.run().each(function(eachResult) {
if (main.hasEnoughUsage()) {
try {
fetchTrackingInfo = trackingStatus = null;
var currentStatus = null;
var description = null;
eachResult = dataSets.formatSingleResult(eachResult, upsitemFulfillmentSearch.columns);
log.debug('eachResult ups', eachResult);
//Request ups for Tracking Information
fetchTrackingInfo = UPSAPIObj.fetchTrackingInfoUps((eachResult.TrackingNumber).toString().trim().split(" ")[0], eachResult.ShipmentAccountNumber);
log.debug('fetchTrackingInfo ups', fetchTrackingInfo);
//Fetch 'StausCode' from FedEx Response
if (fetchTrackingInfo) {
trackingStatus = main.NS_XML_PARSER(fetchTrackingInfo, 'StatusCode');
//trackingStatusDes = main.NS_XML_PARSER(fetchTrackingInfo, 'StatusType');
}
log.debug('trackingStatus ups', trackingStatus);
if (checkForParameter(trackingStatus)) {
if ((assignDefaultValue(trackingStatus.length)) == 0) {
currentStatus = 'OC';
} else if ((assignDefaultValue(trackingStatus.length)) == 1) {
if ((trackingStatus[0].textContent) != 'MP')
currentStatus = 'OC';
else
currentStatus = 'MP';
} else if ((assignDefaultValue(trackingStatus.length)) >= 1) {
currentStatus = trackingStatus[0].textContent;
// description = trackingStatusDes[0].textContent;
}
} else
currentStatus = 'OC';
if (currentStatus) {
record.submitFields({
type: record.Type.ITEM_FULFILLMENT,
id: eachResult.internalid,
values: {
custbody_tracking_status: currentStatus.toString().trim() //Tracking Status
},
options: {
enableSourcing: false,
ignoreMandatoryFields: true
}
});
return true;
}
} catch (erIF) {
log.debug('erIF', erIF);
}
//noTrackingInformation.push(main.generateReferenceToRecord(eachResult.internalid, eachResult.PackageIdentifier));
return true;
}
return false;
});
},
uspsDetails: function(scriptContext) {
//Fetch and Item Fulfillment
var uspsitemFulfillmentSearch = dataSets.uspsItemFulfillment();
log.debug('uspsitemFulfillmentSearch', uspsitemFulfillmentSearch);
uspsitemFulfillmentSearch.searchObj.run().each(function(eachResult) {
if (main.hasEnoughUsage()) {
try {
fetchTrackingInfo = trackingStatus = null;
var currentStatus = null;
eachResult = dataSets.formatSingleResult(eachResult, uspsitemFulfillmentSearch.columns);
log.debug('eachResult usps', eachResult);
//Request usps for Tracking Information
fetchTrackingInfo = USPSAPIObj.fetchTrackingInfoUsps((eachResult.TrackingNumber).toString().trim().split(" ")[0], eachResult.ShipmentAccountNumber);
log.debug('fetchTrackingInfo usps', fetchTrackingInfo);
//Fetch 'StausCode' from FedEx Response
if (fetchTrackingInfo) {
trackingStatus = main.NS_XML_PARSER(fetchTrackingInfo, 'TrackDetail');
}
log.debug('trackingStatus usps', trackingStatus);
if (checkForParameter(trackingStatus)) {
if ((assignDefaultValue(trackingStatus.length)) == 0) {
currentStatus = 'OC';
} else if ((assignDefaultValue(trackingStatus.length)) == 1) {
trackSta = (trackingStatus[0].textContent).split(",", 2);
if ((trackSta[0] == "Shipping Label Created") && (trackSta[1] == "USPS Awaiting Item"))
currentStatus = 'OC';
} else if ((assignDefaultValue(trackingStatus.length)) >= 1) {
currentSt = (trackingStatus[0].textContent).split(",", 1);
currentStatus = currentSt[0];
// description = trackingStatusDes[0].textContent;
}
} else
currentStatus = 'OC';
if (currentStatus) {
log.debug('currentStatus', currentStatus);
record.submitFields({
type: record.Type.ITEM_FULFILLMENT,
id: eachResult.internalid,
values: {
custbody_tracking_status: currentStatus.toString().trim() //Tracking Status
},
options: {
enableSourcing: false,
ignoreMandatoryFields: true
}
});
return true;
}
} catch (erIF) {
log.debug('erIF', erIF);
}
// noTrackingInformation.push(main.generateReferenceToRecord(eachResult.internalid, eachResult.PackageIdentifier));
return true;
}
return false;
});
},
execute: function(scriptContext) {
if (main.isSandBox()) //If current executing environment is SandBox, then disregard the entire process
return false;
log.debug('*** START ***', '*** SCHEDULED SCRIPT STARTS ***');
//Initialize Scheduler Functions
scheduler.run("initialise");
// main.fedexDetails();
main.upsDetails();
main.uspsDetails();
log.debug('*** END ***', '*** SCHEDULED SCRIPT ENDS ***');
return false;
}
};
applyTryCatch(main, "main");
return main;
});