This script is used to auto populate the transit time details in item fulfillment record based on the change in the shipping carrier. The deatils are fetched from FedEx and UPS API.
function (https, log, record, search) {
/**
* Function to be executed when field is changed.
*
* @param {Object} scriptContext
* @param {Record} scriptContext.currentRecord – Current form record
* @param {string} scriptContext.sublistId – Sublist name
* @param {string} scriptContext.fieldId – Field name
* @param {number} scriptContext.lineNum – Line number. Will be undefined if not a sublist or matrix field
* @param {number} scriptContext.columnNum – Line number. Will be undefined if not a matrix field
*
* @since 2015.2
*/
function fieldChanged(scriptContext) {
try {
if (scriptContext.fieldId === ‘shipcarrier’) {
let currentRecord = scriptContext.currentRecord;
let subsidiary = currentRecord.getValue({ fieldId: ‘subsidiary’ })
// Check if the subsidiary is either 3 or 7
if (subsidiary == 3 || subsidiary == 7) {
let shippingCarrier = currentRecord.getValue(‘shipcarrier’);
let tranDate = currentRecord.getValue({ fieldId: ‘trandate’ });
let transactionDateObject = currentRecord.getValue({ fieldId: ‘trandate’ });
let IFDateString = transactionDateObject.toISOString();
let transactionDate = IFDateString.split(‘T’)[0];
let relatedSalesOrder = currentRecord.getValue({ fieldId: ‘createdfrom’ });
let detailsSO = salesOrderSearch(relatedSalesOrder);
let shipToCountry = detailsSO.shipCountry;
let shipToZip = detailsSO.shipZip;
let location = detailsSO.location;
let totalAmount = detailsSO.total;
let packageWeight = getPackageWeight(currentRecord)
let shipFromCountry, shipFromZip, locationDetails;
if (location) {
locationDetails = getLocationDetails(location);
shipFromCountry = locationDetails.country;
shipFromZip = locationDetails.zipCode;
}
let serviceDetails;
if (shippingCarrier === ‘nonups’) {
let configDetailsFedEx = getConfigDetails(1);
if (configDetailsFedEx.access_token) {
let shipToCountry = currentRecord.getValue({ fieldId: ‘shipcountry’ });
serviceDetails = getTransitTimeDetailsFedEx(configDetailsFedEx, shipFromCountry, shipFromZip, shipToZip, shipToCountry, transactionDate, totalAmount, packageWeight);
}
} else if (shippingCarrier === ‘ups’) {
let configDetailsUPS = getConfigDetails(2);
if (configDetailsUPS.access_token) {
let shipToCountry = currentRecord.getValue({ fieldId: ‘shipcountry’ });
serviceDetails = getTransitTimeDetailsUPS(configDetailsUPS, shipFromCountry, shipFromZip, shipToZip, shipToCountry, transactionDate, packageWeight);
}
}
if (serviceDetails) {
let htmlContent = buildTransitTimeHtml(serviceDetails)
currentRecord.setValue({
fieldId: ‘custbody_jj_transit_details’,
value: htmlContent
});
}
else {
currentRecord.setValue({
fieldId: ‘custbody_jj_transit_details’,
value: ‘No transit time details available.’
});
}
} else {
console.log(‘Subsidiary not 3 or 7’, `Skipping processing for subsidiary ${subsidiary}`);
}
}
}
catch (error) {
console.error({
title: ‘Error fetching transit time details’,
details: error.toString()
});
alert(‘An error occurred while fetching transit time details. Please try again.’);
}
}
/**
* @description Get the package weight for a Sales Order.
* @param {Object} newRecord – The Sales Order record object.
* @returns {number} – The total package weight.
*/
function getPackageWeight(currentRecord) {
try {
let totalPackageWeight = 0;
// Get the number of item lines
let lineCount = currentRecord.getLineCount({ sublistId: ‘item’ });
// Iterate over each item line
for (let i = 0; i < lineCount; i++) {
// Get the package weight (custcol_jj_ee_pckg_wght) and quantity for the line
let packageWeight = currentRecord.getSublistValue({
sublistId: ‘item’,
fieldId: ‘custcol_jj_ee_pckg_wght’,
line: i
});
let quantity = currentRecord.getSublistValue({
sublistId: ‘item’,
fieldId: ‘quantity’,
line: i
});
if (!packageWeight || isNaN(packageWeight)) {
packageWeight = 0;
}
// Check if quantity is null, undefined, or not a number
if (!quantity || isNaN(quantity)) {
quantity = 0;
}
totalPackageWeight += (packageWeight * quantity);
}
return totalPackageWeight;
} catch (e) {
console.error(‘Error calculating total package weight’, e);
return 0;
}
}
/**
* @description Get location details from location ID.
* @param {number} locationId – The internal ID of the location.
* @returns {Object|null} – The location details or null if an error occurs.
*/
function getLocationDetails(locationId) {
try {
// Use search.lookupFields to retrieve the country and zip fields from the location record
let locationDetails = search.lookupFields({
type: search.Type.LOCATION,
id: locationId,
columns: [‘country’, ‘zip’] // Fields you want to retrieve
});
// Extract the country and zip from the returned object
let country = locationDetails.country;
let zipCode = locationDetails.zip;
return {
country: country,
zipCode: zipCode
};
} catch (e) {
console.error(‘Error retrieving location details’, e.toString());
return null;
}
}
/**
* @description Retrieves configuration details from the custom record `customrecord_jj_fedex_api_details` based on the provided record ID.
* @param {number} recordId – The internal ID of the custom record containing API configuration details.
* @returns {Object} configObj – An object containing the API configuration details, or an empty object if an error occurs.
*/
const getConfigDetails = (recordId) => {
try {
let configObj = {};
let customrecord_jj_fedex_api_detailsSearchObj = search.create({
type: “customrecord_jj_fedex_api_details”,
filters: [[“internalid”, “anyof”, recordId]],
columns: [
“custrecord_jj_client_id”,
“custrecord_jj_client_secret”,
“custrecord_jj_api_endpoint_url”,
“custrecord_jj_api_key”,
“custrecord_jj_account_number”,
“custrecord_jj_access_token”
]
});
let searchResultCount = customrecord_jj_fedex_api_detailsSearchObj.runPaged().count;
if (searchResultCount > 0) {
customrecord_jj_fedex_api_detailsSearchObj.run().each(function (result) {
configObj.client_id = result.getValue(“custrecord_jj_client_id”);
configObj.client_secret = result.getValue(“custrecord_jj_client_secret”);
configObj.posturl = result.getValue(“custrecord_jj_api_endpoint_url”);
configObj.api_key = result.getValue(“custrecord_jj_api_key”);
configObj.account_no = result.getValue(“custrecord_jj_account_number”);
configObj.access_token = result.getValue(“custrecord_jj_access_token”);
return true;
});
}
return configObj;
} catch (e) {
console.error(“Error@getConfigDetails”, e);
return {};
}
};
/**
* Sends a POST request to the FedEx API to retrieve transit time details for a shipment.
*
* @param {Object} configDetailsFedEx – Configuration details for the FedEx API, including the access token and account number.
* @param {string} shipFromCountry – The country code of the origin address.
* @param {string} shipFromZip – The postal code of the origin address.
* @param {string} shipToZip – The postal code of the destination address.
* @param {string} shipToCountry – The country code of the destination address.
* @param {string} transactionDate – The date of the shipment.
* @param {number} packageWeight – The weight of the package in pounds.
* @param {number} totalAmount – The total value of the shipment.
* @returns {Object} An object containing the transit time details extracted from the FedEx API response.
*/
const getTransitTimeDetailsFedEx = (configDetailsFedEx, shipFromCountry, shipFromZip, shipToZip, shipToCountry, transactionDate, totalAmount, packageWeight) => {
try {
const headers = {
‘Accept’: ‘application/json’,
‘Content-Type’: ‘application/json’,
‘Authorization’: ‘Bearer ‘ + configDetailsFedEx.access_token,
};
// Set up the request body
const body = {
“accountNumber”: {
“value”: configDetailsFedEx.account_no
},
“rateRequestControlParameters”: {
“returnTransitTimes”: true,
“servicesNeededOnRateFailure”: true
},
“requestedShipment”: {
“shipper”: {
“address”: {
“postalCode”: shipFromZip,
“countryCode”: shipFromCountry
}
},
“rateRequestType”: [
“ACCOUNT”,
“LIST”
],
“recipient”: {
“address”: {
“postalCode”: shipToZip,
“countryCode”: shipToCountry
}
},
“shipDateStamp”: transactionDate,
“pickupType”: “DROPOFF_AT_FEDEX_LOCATION”,
“requestedPackageLineItems”: [
{
“weight”: {
“units”: “LB”,
“value”: packageWeight
},
},
],
}
};
let url = ‘https://apis.fedex.com/rate/v1/rates/quotes’;
const response = https.request({
method: https.Method.POST,
url: url,
headers: headers,
body: JSON.stringify(body)
});
console.log({
title: ‘FedEx API Response’,
details: response.body
});
let transitTimeDetails = ”;
if (response.code === 200) {
let responseObj = JSON.parse(response.body);
transitTimeDetails = extractTransitTimeDetails(responseObj);
}
return transitTimeDetails;
} catch (e) {
console.error(“FedEx Error@getTransitTimeDetailsFedEx”, e);
return {};
}
};
/**
* @description Extracts and formats transit time details from the FedEx API response object.
* @param {Object} responseObj – The response object returned from the FedEx API containing rate reply details.
* @returns {Array} serviceDetails – An array of objects, each containing shipping method, delivery date, delivery day, and delivery time.
*/
function extractTransitTimeDetails(responseObj) {
try {
let rateReplyDetails = responseObj.output.rateReplyDetails;
let serviceDetails = [];
// Iterate over rateReplyDetails to extract relevant details
rateReplyDetails.forEach(function (rateReplyDetail) {
let deliveryDateTime = rateReplyDetail.operationalDetail.deliveryDate;
let timePart = deliveryDateTime.split(‘T’)[1]; // Extract time part (e.g., “09:00:00”)
let datePart = deliveryDateTime.split(‘T’)[0]; // Extract date part
let details = {
shippingMethod: rateReplyDetail.serviceName,
deliveryDate: datePart,
deliveryDay: rateReplyDetail.operationalDetail.deliveryDay,
deliveryTime: timePart
};
serviceDetails.push(details);
});
return serviceDetails;
} catch (e) {
console.error(“Error @ extractTransitTimeDetails”, e.toString());
}
}
/**
* @description Fetches transit time details from the UPS API based on shipment information.
* @param {Object} configDetailsUPS – Configuration details for UPS API access, including access token.
* @param {string} shipFromCountry – Country code of the shipment origin.
* @param {string} shipFromZip – Postal code of the shipment origin.
* @param {string} shipToZip – Postal code of the shipment destination.
* @param {string} shipToCountry – Country code of the shipment destination.
* @param {string} transactionDate – Date of the shipment in YYYY-MM-DD format.
* @param {number} packageWeight – Weight of the package in pounds.
* @returns {Array|string} – An array of service details if the request is successful, otherwise an error message.
*/
const getTransitTimeDetailsUPS = (configDetailsUPS, shipFromCountry, shipFromZip, shipToZip, shipToCountry, transactionDate, packageWeight) => {
try {
// Setup the headers
let headers = {
‘transId’: ” “,
‘transactionSrc’: ‘testing’,
‘Content-Type’: ‘application/json’,
‘Accept’: ‘application/json’,
‘Authorization’: ‘Bearer ‘ + configDetailsUPS.access_token
};
// Setup the request body
let body = {
“originCountryCode”: shipFromCountry,
“originPostalCode”: shipFromZip,
“destinationCountryCode”: shipToCountry,
“destinationPostalCode”: shipToZip,
“weight”: packageWeight,
“weightUnitOfMeasure”: “LBS”,
“shipDate”: transactionDate
};
let url = ‘https://onlinetools.ups.com/api/shipments/v1/transittimes’;
// Send the request to UPS API
let response = https.request({
method: https.Method.POST,
url: url,
headers: headers,
body: JSON.stringify(body)
});
let serviceDetails, tableUPS;
if (response.code === 200) {
let responseBody = JSON.parse(response.body);
serviceDetails = extractServiceDetails(responseBody);
} else {
console.error(“UPS API Error”, response.body);
return “Error fetching transit time”;
}
return serviceDetails;
} catch (e) {
console.error(“Error@getTransitTimeDetailsUPS”, e);
return “Error fetching transit time”;
}
};
/**
* @description Extracts service details from the UPS API response.
* @param {Object} responseBody – The response body from the UPS API.
* @returns {Array<Object>} serviceDetails – An array of service details.
*/
function extractServiceDetails(responseBody) {
var emsResponse = responseBody.emsResponse;
var services = emsResponse.services;
var serviceDetails = [];
services.forEach(function (service) {
var details = {
shippingMethod: service.serviceLevelDescription,
deliveryDate: service.deliveryDate,
deliveryTime: service.deliveryTime,
deliveryDay: service.deliveryDayOfWeek
};
serviceDetails.push(details);
});
return serviceDetails;
}
/**
* @description Builds an HTML string to display transit time details in a styled format.
* @param {Array<Object>} serviceDetails – An array of service details to be displayed.
* @returns {String} htmlContent – The generated HTML content.
*/
function buildTransitTimeHtml(serviceDetails) {
try {
if (!serviceDetails || serviceDetails.length === 0) {
return ‘<p>No transit time details available.</p>’;
}
let htmlContent = `
<div class=”fedex-container”>
<style>
.fedex-container {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin: 20px;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
background-color: #f9f9f9;
}
.fedex-box {
flex: 1 1 30%;
box-sizing: border-box;
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
background-color: #fff;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.fedex-box p {
margin-bottom: 0;
font-size: 14px;
color: #666;
}
</style>
`;
serviceDetails.forEach(detail => {
let shippingMethod = detail.shippingMethod || ‘N/A’;
let deliveryDate = detail.deliveryDate || ‘N/A’;
let deliveryTime = detail.deliveryTime || ‘N/A’;
let deliveryDay = detail.deliveryDay || ‘N/A’;
htmlContent += `
<div class=”fedex-box”>
<p><strong>Shipping Method:</strong> ${shippingMethod}</p>
<p><strong>Day:</strong> ${deliveryDay}</p>
<p><strong>Date:</strong> ${deliveryDate}</p>
<p><strong>Time:</strong> ${deliveryTime}</p>
</div>
`;
});
htmlContent += ‘</div>’;
return htmlContent;
} catch (error) {
console.error(‘Error generating transit time HTML:’, error);
return `<p>An error occurred while retrieving transit time details: ${error.message || error}</p>`;
//return ‘<p>An error occurred while retrieving transit time details.</p>’;
}
}
/**
* @description Performs a search for a specific sales order based on the given internal ID and returns relevant shipping and order details.
* @param {number|string} relatedSalesOrder – The internal ID of the sales order to search for.
* @returns {Object|null} salesOrderData – An object containing details of the sales order if found, otherwise null.
*/
function salesOrderSearch(relatedSalesOrder) {
try {
let salesOrderSearchObj = search.create({
type: “salesorder”,
filters: [
[“type”, “anyof”, “SalesOrd”], “AND”,
[“internalid”, “anyof”, relatedSalesOrder], “AND”,
[“taxline”, “is”, “F”], “AND”,
[“cogs”, “is”, “F”], “AND”,
[“shipping”, “is”, “F”]
],
columns: [
search.createColumn({ name: “statusref”, label: “Status” }),
search.createColumn({ name: “shipcountrycode”, label: “Shipping Country Code” }),
search.createColumn({ name: “shipzip”, label: “Shipping Zip” }),
search.createColumn({ name: “location”, label: “Location” }),
search.createColumn({ name: “total”, label: “Total” }),
search.createColumn({ name: “shipstate”, label: “Shipping State/Province” }),
search.createColumn({ name: “shipcity”, label: “Shipping City” }),
search.createColumn({ name: “shipaddress2”, label: “Shipping Address 2” }),
search.createColumn({ name: “shipaddress1”, label: “Shipping Address 1” }),
search.createColumn({ name: “shipaddressee”, label: “Shipping Addressee” }),
search.createColumn({ name: “shippingattention”, label: “Shipping Attention” })
]
});
let salesOrderData = {};
salesOrderSearchObj.run().each(function (result) {
salesOrderData.status = result.getText({ name: “statusref”, label: “Status” });
salesOrderData.shipCountry = result.getValue({ name: “shipcountrycode”, label: “Shipping Country Code” });
salesOrderData.shipZip = result.getValue({ name: “shipzip”, label: “Shipping Zip” });
salesOrderData.location = result.getValue({ name: “location”, label: “Location” });
salesOrderData.total = result.getValue({ name: “total”, label: “Total” });
salesOrderData.shipState = result.getValue({ name: “shipstate”, label: “Shipping State/Province” });
salesOrderData.shipCity = result.getValue({ name: “shipcity”, label: “Shipping City” });
salesOrderData.shippingAdd2 = result.getValue({ name: “shipaddress2”, label: “Shipping Address 2” });
salesOrderData.shippingAdd1 = result.getValue({ name: “shipaddress1”, label: “Shipping Address 1” });
salesOrderData.shippingAddressee = result.getValue({ name: “shipaddressee”, label: “Shipping Addressee” });
salesOrderData.shippingAttention = result.getValue({ name: “shippingattention”, label: “Shipping Attention” });
return false;
});
return salesOrderData;
} catch (e) {
console.error(“error in salesOrderSearch”, e);
return null;
}
}
return {
fieldChanged: fieldChanged
};
});