NetSuite-Dispatch Tracker Scheduled Order Integration

The client needs to make orders in the dispatch tracker system based on the orders scheduled in NetSuite.

/**
* @NApiVersion 2.1
* @NScriptType MapReduceScript
*/
/************************************************************************************************
* * Map/Reduce Script For Sending SO Details to Delivery Dispatcher **
*
*
* **********************************************************************************************
*
* Author: Jobin and Jismi IT Services
*
* Date Created : 26-August-2022
*
* Created By: Athul Krishna, Jobin and Jismi IT Services
*
* Description : Map/Reduce Script For Sending SO Details to Delivery Dispatcher
*
* REVISION HISTORY
*
*
*
*
***********************************************************************************************/
define(['N/error', 'N/http', 'N/record', 'N/runtime', 'N/search', 'N/url', '../Library/JJ CM Dispatch Tracker API AHAP-14.js', '../Library/JJ CM NS Utility'],
/**
* @param{error} error
* @param{http} http
* @param{record} record
* @param{runtime} runtime
* @param{search} search
* @param{url} url
*/
(error, http, record, runtime, search, url, dispatchTracker, JJ_CM_NS_Utility) => {
/**
* 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 {
const currentEnv = JJ_CM_NS_Utility.envMethods.init();
if (!currentEnv?.envType?.PRODUCTION)
return [];
let salesorderSearchObj = search.create({
type: "transaction",
filters: [
[
[
["type", "anyof", "TrnfrOrd"],
"AND",
["transactionlinetype", "anyof", "ITEM"]
],
"OR",
["type", "anyof", "RtnAuth", "SalesOrd"]
],
"AND",
["status", "anyof", "RtnAuth:B", "RtnAuth:D", "RtnAuth:E", "RtnAuth:F", "SalesOrd:B", "SalesOrd:D", "SalesOrd:E", "SalesOrd:F", "TrnfrOrd:B", "TrnfrOrd:F", "TrnfrOrd:E", "TrnfrOrd:G"],
"AND",
["mainline", "is", "F"],
"AND",
["custcol_sent_2_dt", "isempty", ""],
"AND",
["custcol_dt_order", "isnotempty", ""],
"AND",
["custcol_del_conf", "is", "F"]
],
columns: [
search.createColumn({name: "item", label: "Item"}),
search.createColumn({
name: "formulatext",
formula: "{internalid}||':'||{item}",
label: "Line Key"
}),
search.createColumn({
name: "line",
sort: search.Sort.ASC,
label: "Line ID"
}),
search.createColumn({name: "custcol_dt_order", label: "DT Order"}),
search.createColumn({name: "custcol_sched_del_date", label: "Req Del Date"}),
search.createColumn({
name: "displayname",
join: "item",
label: "Display Name"
}),
search.createColumn({name: "quantity", label: "Quantity"}),
search.createColumn({name: "custcol_trans_route", label: "Route"}),
search.createColumn({
name: "externalid",
join: "customer",
label: "External ID"
}),
search.createColumn({
name: "firstname",
join: "customer",
label: "First Name"
}),
search.createColumn({
name: "lastname",
join: "customer",
label: "Last Name"
}),
search.createColumn({
name: "phone",
join: "customer",
label: "Phone"
}),
search.createColumn({
name: "mobilephone",
join: "customer",
label: "Mobile Phone"
}),
search.createColumn({name: "email", label: "Email"}),
search.createColumn({
name: "shipaddress1",
join: "customer",
label: "Shipping Address 1"
}),
search.createColumn({
name: "shipaddress2",
join: "customer",
label: "Shipping Address 2"
}),
search.createColumn({
name: "shipcity",
join: "customer",
label: "Shipping City"
}),
search.createColumn({
name: "shipstate",
join: "customer",
label: "Shipping State/Province"
}),
search.createColumn({
name: "shipzip",
join: "customer",
label: "Shipping Zip"
}),
search.createColumn({
name: "shipphone",
join: "customer",
label: "Shipping Phone"
}),
search.createColumn({name: "memomain", label: "Memo (Main)"}),
search.createColumn({
name: "internalid",
join: "location",
label: "Location"
}),
//search.createColumn({name: "custcol_line_notes", label: "Line Notes"}),
search.createColumn({name: "salesrep", label: "Sales Rep"}),
search.createColumn({
name: "formulatext",
formula: "TO_CHAR(sysdate,'M/D/YYYY')",
label: "Sent To DT"
}),
search.createColumn({name: "custcol_dt_acct", label: "DT Account"}),
search.createColumn({
name: "internalid",
join: "customer",
label: "Customer Internal"
}),
search.createColumn({name: "custcol_sched_del_date", label: "Req Del Date"}),
search.createColumn({
name: "itemid",
join: "item",
label: "Name"
}),
search.createColumn({name: "amount", label: "Amount"}),
//search.createColumn({ name: "custcol_aha_item_description", label: "Item Description" }),
search.createColumn({name: "custcol_trans_route", label: "Route"}),
search.createColumn({
name: "custbody_jj_delivery_instructions",
label: "Delivery Instructions"
}),
search.createColumn({name: "custbody_jj_type_of_delivery", label: "Type of Delivery"}),
search.createColumn({name: "custbody_jj_property_type", label: "Property Type"}),
search.createColumn({name: "custbody_jj_house_type", label: "House Type"}),
search.createColumn({name: "custbody_delivery_floor_level", label: "Delivery Floor Level"}),
search.createColumn({name: "custbody_jj_number_of_stairs", label: "Number of Stairs"}),
search.createColumn({name: "custbody_jj_labor_requirements", label: "Labor Requirements"}),
search.createColumn({name: "custbody_jj_fuel_type", label: "Fuel Type"}),
search.createColumn({
name: "salesdescription",
join: "item",
label: "Description"
}),
search.createColumn({
name: "custitem30",
join: "item",
label: "Height (IN)"
}),
search.createColumn({
name: "custitem29",
join: "item",
label: "Width (IN)"
}),
search.createColumn({
name: "custitem28",
join: "item",
label: "Depth (IN)"
}),
search.createColumn({
name: "formulanumeric",
formula: "CASE WHEN ((TO_NUMBER({item.custitem28}) IS NULL OR TO_NUMBER({item.custitem28})=0) AND (TO_NUMBER({item.custitem29}) IS NULL OR TO_NUMBER({item.custitem29})=0) AND (TO_NUMBER({item.custitem30}) IS NULL OR TO_NUMBER({item.custitem30})=0)) THEN 0 ELSE NVL(TO_NUMBER({item.custitem29}),1)*NVL(TO_NUMBER({item.custitem28}),1)*NVL(TO_NUMBER({item.custitem30}),1)END",
label: "Formula (Numeric)"
}),
search.createColumn({
name: "custitem44",
join: "item",
label: "Installation Time"
}),
search.createColumn({name: "custcol_dt_acct", label: "DT Account"}),
search.createColumn({name: "type", label: "Type"}),
search.createColumn({name: "tranid", label: "Document Number"}),
search.createColumn({name: "memo", label: "Memo"}),
search.createColumn({name: "salesrep", label: "Sales Rep"}),
search.createColumn({name: "custcol_trans_route", label: "Route"}),
search.createColumn({name: "shipcity", label: "Shipping City"}),
search.createColumn({name: "shipaddress1", label: "Shipping Address 1"}),
search.createColumn({name: "shipaddress2", label: "Shipping Address 2"}),
search.createColumn({name: "shipstate", label: "Shipping State/Province"}),
search.createColumn({name: "shipzip", label: "Shipping Zip"}),
search.createColumn({name: "shipcountry", label: "Shipping Country"}),
search.createColumn({
name: "altname",
join: "customer",
label: "Name"
}),
search.createColumn({name: "custcol_jj_scheduled_by", label: "Scheduled By"}),
search.createColumn({name: "custbody15", label: "Driver Call Ahead Number"}),
search.createColumn({name: "custbody8", label: "Bill to Main Number"}),
search.createColumn({name: "location", label: "Location"}),
search.createColumn({name: "custbody_aha_sales_location", label: "Sales Location"})


]
});
let searchResultCount = salesorderSearchObj.runPaged().count;
log.debug("salesorderSearchObj result count", searchResultCount);
if (searchResultCount > 0) {
return salesorderSearchObj
} else {
return []
}
} catch (e) {
log.error("error@getInput", e);
}
}

/**
* Defines the function that is executed when the map entry point is triggered. This entry point is triggered automatically
* when the associated getInputData stage is complete. This function is applied to each key-value pair in the provided
* context.
* @param {Object} mapContext - Data collection containing the key-value pairs to process in the map stage. This parameter
* is provided automatically based on the results of the getInputData stage.
* @param {Iterator} mapContext.errors - Serialized errors that were thrown during previous attempts to execute the map
* function on the current key-value pair
* @param {number} mapContext.executionNo - Number of times the map function has been executed on the current key-value
* pair
* @param {boolean} mapContext.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} mapContext.key - Key to be processed during the map stage
* @param {string} mapContext.value - Value to be processed during the map stage
* @since 2015.2
*/

const map = (mapContext) => {
try {
let searchResult = JSON.parse(mapContext.value);
//log.debug("ssearchResult",searchResult)
if (searchResult && searchResult?.values?.custcol_dt_order) {
let dtOrderNumber = searchResult.values.custcol_dt_order;
//log.debug("dtOrderNumber",dtOrderNumber)
mapContext.write({
key: dtOrderNumber,
value: searchResult
});
}
} catch (e) {
log.debug("error@Map", e)
}

}

function checkForParameter(parameter) {
if (parameter !== "" && parameter !== null && parameter !== undefined && parameter !== false && parameter !== "null"
&& parameter !== "undefined" && parameter !== " " && parameter !== 'false') {
return true;
} else {
log.debug("empty")
return false;
}
}

function dispatchTrackerIntegrationLogSearch(dtNUmber) {
try {
let customrecord_jj_dispatcher_log_ahap14SearchObj = search.create({
type: "customrecord_jj_dispatcher_log_ahap14",
filters: [
["custrecord_jj_dt_number_ahap_14", "is", dtNUmber]
],
columns: [
search.createColumn({name: "custrecord_jj_dt_number_ahap_14", label: "DT Number"}),
search.createColumn({name: "custrecord_jj_resolved_check_box_ahap_14", label: "Resolved"}),
search.createColumn({name: "internalid", label: "Internal ID"})
]
});
let searchResultCount = customrecord_jj_dispatcher_log_ahap14SearchObj.runPaged().count;
if (searchResultCount > 0) {
let internalID = false;
log.debug("customrecord_jj_dispatcher_log_ahap14SearchObj result count", searchResultCount);
customrecord_jj_dispatcher_log_ahap14SearchObj.run().each(function (result) {
internalID = result.getValue({
name: "internalid", label: "Internal ID"
})
return false;
});
return internalID;
} else {
return false;
}
} catch (e) {
log.debug("error@dispatchTrackerIntegrationLogSearch", e);
return false;
}
}


function escapeXMLData(data) {
if (data?.toString()?.trim()) {
return data.toString().trim().replaceAll('&', " ").replaceAll('<', ' ').replaceAll('>', ' ').replaceAll('?', ' ').replaceAll('%', ' ').replaceAll('{', ' ').replaceAll('}', ' ').replaceAll('\n\r', ' ').replaceAll('\r\n', ' ').replaceAll('\n', ' ').replaceAll('\r', ' ').replaceAll('"', ' ').replaceAll('\'', ' ')
}
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
* for processing
* @since 2015.2
*/
const reduce = (reduceContext) => {
try {
let key = reduceContext.key
log.debug("key", key)
let reduceData = reduceContext.values.map(JSON.parse);
log.debug("reduce data", reduceData)

let address1 = reduceData[0].values["shipaddress1"]
let state = reduceData[0].values["shipstate"]
let city = reduceData[0].values["shipstate"]
let zip = reduceData[0].values["shipzip"]

if (address1 && state && city && zip) {
let delivery_Type = ''
if (reduceData[0].values["custbody_jj_type_of_delivery"] && reduceData[0].values["custbody_jj_type_of_delivery"]?.text) {
delivery_Type = reduceData[0].values["custbody_jj_type_of_delivery"]?.text;
if ((delivery_Type.toLowerCase()).includes('pick up')) {
delivery_Type = "Pickup"
} else {
delivery_Type = "Delivery"
}
} else {
delivery_Type = "Delivery"
}
let emailId = reduceData[0].values["email"];
let firstName = reduceData[0].values["firstname.customer"];
if (firstName == '' || !firstName) {
firstName = reduceData[0].values['altname.customer'];
}
if (!firstName && reduceData[0].recordType == 'transferorder' && reduceData[0].values.custcol_jj_scheduled_by?.value) {
let customerEmail = search.lookupFields({
type: search.Type.EMPLOYEE,
id: reduceData[0].values.custcol_jj_scheduled_by.value,
columns: ['email', 'altname']
});
emailId = customerEmail.email;
firstName = customerEmail.altname;
if ((!firstName || firstName === "") && emailId) {
firstName = emailId.split("@")[0];
}


}
log.debug("emailId,firstName", {emailId, firstName});

let dtAccount = reduceData[0].values["custcol_dt_acct"].text;
//let type=delivery_Type
log.debug("delievery Type", delivery_Type)


let headerObj = {
number: reduceData[0].values.custcol_dt_order,
account: "trialsairporthomeappliance",
service_type: delivery_Type,
description: escapeXMLData(reduceData[0].values['custbody_jj_delivery_instructions']),
customer_id: reduceData[0].values["internalid.customer"].value,
first_name: firstName,
last_name: reduceData[0].values["lastname.customer"],
email: emailId,
phone1: reduceData[0].values["custbody15"],
phone2: reduceData[0].values["custbody8"],
address1: escapeXMLData(reduceData[0].values["shipaddress1"]),
address2: escapeXMLData(reduceData[0].values["shipaddress2"]),
city: escapeXMLData(reduceData[0].values["shipcity"]),
state: reduceData[0].values["shipstate"],
zip: reduceData[0].values["shipzip"],
latitude: "",
longitude: "",
email_opt_out: "false",
preferred_contact_method: "ALL",


}

let footerObj = {
preferred_contact_method: "ALL",
request_delivery_date: reduceData[0].values["custcol_sched_del_date"],
service_unit: reduceData[0].values["custcol_trans_route"]?.text,
af_route_label: "Daily",
af_account: reduceData[0].values["custcol_dt_acct"].text


}
let sales_person = reduceData[0].values['salesrep']?.text
if (!checkForParameter(sales_person)) {
sales_person = ''
}
let additionField = {
delivery_instruction: escapeXMLData(reduceData[0].values["custbody_jj_delivery_instructions"]),
property_type: reduceData[0].values["custbody_jj_property_type"].text,
house_type: reduceData[0].values["custbody_jj_house_type"].text,
delivery_floor_level: reduceData[0].values["custbody_delivery_floor_level"].text,
number_of_stairs: reduceData[0].values["custbody_jj_number_of_stairs"].text,
delivery_type: reduceData[0].values["custbody_jj_type_of_delivery"].text,
fuel_type: reduceData[0].values["custbody_jj_fuel_type"].text,
ns_type: reduceData[0].recordType,
internal_id: reduceData[0].id,
account: dtAccount,
sales_person: escapeXMLData(sales_person),
document_number: reduceData[0].values['tranid'],
memo: escapeXMLData(reduceData[0].values['memo']),
route_label: reduceData[0].values['custcol_trans_route'].text,
sales_location: reduceData[0].values['custbody_aha_sales_location'].text,
}

let itemsArray = [];
for (let i = 0; i < reduceData.length; i++) {
log.debug("cube", reduceData[i].values['formulanumeric'])
let quantity = reduceData[i].values["quantity"];
let description = escapeXMLData(reduceData[i].values['salesdescription.item'])
if (reduceData[0].recordType == 'transferorder')
quantity = Math.abs(quantity)
itemsArray.push({
line: reduceData[i].values["line"],
itemCode: escapeXMLData(reduceData[i].values["item"].text + ":" + reduceData[i].values["line"]),
sale_sequence: reduceData[i].values["line"],
item_id: escapeXMLData(reduceData[i].values["item"].text),
description: escapeXMLData(description),
line_item_notes: "", //escapeXMLData(reduceData[i].values["custcol_line_notes"]),
quantity: quantity,
location: reduceData[i].values["location"].text,
price: reduceData[i].values["amount"],
setup_time: reduceData[i].values['custitem44.item'],
cube: reduceData[i].values['formulanumeric'],
})

}
let mainRequest = dispatchTracker.serviceOrder.generateRequestBody(headerObj, itemsArray, footerObj, additionField)
log.debug("Main Request in Reduce", mainRequest)

if (mainRequest) {
// let apiResponse = DISPATCH_API.createServiceOrder.sendAPIRequest(mainRequest);
let apiResponse = dispatchTracker.serviceOrder.createServiceOrder(mainRequest, dtAccount)
log.debug("apiResponse", apiResponse);
let resposneCode = apiResponse.status
if (resposneCode == 200) {
//let type=reduceData[0]
let currentRecord = record.load({
type: reduceData[0].recordType,
id: reduceData[0].id,
isDynamic: false
});
let lineCount = currentRecord.getLineCount({sublistId: 'item'})
for (let i = 0; i < lineCount; i++) {
let soID = currentRecord.getSublistValue({
sublistId: 'item',
fieldId: 'custcol_dt_order',
line: i
})
if (key == soID) {
currentRecord.setSublistValue({
sublistId: 'item',
fieldId: 'custcol_sent_2_dt',
value: new Date(),
line: i
});
}
}
currentRecord.save({
ignoreMandatoryFields: true
});
let integrationLogRecord = dispatchTrackerIntegrationLogSearch(key)
if (integrationLogRecord && integrationLogRecord != false) {
let logRecord = record.load({
type: 'customrecord_jj_dispatcher_log_ahap14',
id: integrationLogRecord,
isDynamic: false
});
logRecord.setValue({
fieldId: 'custrecord_jj_resolved_check_box_ahap_14',
value: true
});


logRecord.save({
ignoreMandatoryFields: true
});
log.debug("");
}
} else {
let integrationLogRecord = dispatchTrackerIntegrationLogSearch(key)
if (integrationLogRecord && integrationLogRecord != false) {
let logRecord = record.load({
type: 'customrecord_jj_dispatcher_log_ahap14',
id: integrationLogRecord,
isDynamic: false
});
logRecord.setValue({
fieldId: 'custrecord_jj_resolved_check_box_ahap_14',
value: false
});
logRecord.setValue({
fieldId: 'custrecord_jj_error_message_ahap_14',
value: apiResponse.result
})

logRecord.save({
ignoreMandatoryFields: true
});
} else {
let customErrorRecord = record.create({
type: 'customrecord_jj_dispatcher_log_ahap14',
isDynamic: true
});
customErrorRecord.setValue({
fieldId: 'name',
value: reduceData[0].values.custcol_dt_order
})
customErrorRecord.setValue({
fieldId: 'custrecord_jj_dt_number_ahap_14',
value: reduceData[0].values.custcol_dt_order
})
customErrorRecord.setValue({
fieldId: 'custrecord_jj_error_message_ahap_14',
value: apiResponse.result
})
customErrorRecord.setValue({
fieldId: 'custrecord_jj_resolved_check_box_ahap_14',
value: false
});
customErrorRecord.setValue({
fieldId: 'custrecord_jj_transaction_number_ahap_14',
value: reduceData[0].id
});
customErrorRecord.save({
ignoreMandatoryFields: true
})
}
}
}
} else {
log.debug("nothing working")
let integrationLogRecord = dispatchTrackerIntegrationLogSearch(key)
if (integrationLogRecord && integrationLogRecord != false) {
let logRecord = record.load({
type: 'customrecord_jj_dispatcher_log_ahap14',
id: integrationLogRecord,
isDynamic: false
});
logRecord.setValue({
fieldId: 'custrecord_jj_resolved_check_box_ahap_14',
value: false
});
logRecord.setValue({
fieldId: 'custrecord_jj_error_message_ahap_14',
value: "Required fields is missing"
})

logRecord.save({
ignoreMandatoryFields: true
});
} else {
let customErrorRecord = record.create({
type: 'customrecord_jj_dispatcher_log_ahap14',
isDynamic: true
});
customErrorRecord.setValue({
fieldId: 'name',
value: reduceData[0].values.custcol_dt_order
})
customErrorRecord.setValue({
fieldId: 'custrecord_jj_dt_number_ahap_14',
value: reduceData[0].values.custcol_dt_order
})
customErrorRecord.setValue({
fieldId: 'custrecord_jj_error_message_ahap_14',
value: "Required fields is missing"
})
customErrorRecord.setValue({
fieldId: 'custrecord_jj_resolved_check_box_ahap_14',
value: false
});
customErrorRecord.setValue({
fieldId: 'custrecord_jj_transaction_number_ahap_14',
value: reduceData[0].id
});
customErrorRecord.save({
ignoreMandatoryFields: true
})
}
}
} catch (e) {
log.debug("error@Reduce", e)
}

}


/**
* Defines the function that is executed when the summarize entry point is triggered. This entry point is triggered
* automatically when the associated reduce stage is complete. This function is applied to the entire result set.
* @param {Object} summaryContext - Statistics about the execution of a map/reduce script
* @param {number} summaryContext.concurrency - Maximum concurrency number when executing parallel tasks for the map/reduce
* script
* @param {Date} summaryContext.dateCreated - The date and time when the map/reduce script began running
* @param {boolean} summaryContext.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 {Iterator} summaryContext.output - Serialized keys and values that were saved as output during the reduce stage
* @param {number} summaryContext.seconds - Total seconds elapsed when running the map/reduce script
* @param {number} summaryContext.usage - Total number of governance usage units consumed when running the map/reduce
* script
* @param {number} summaryContext.yields - Total number of yields when running the map/reduce script
* @param {Object} summaryContext.inputSummary - Statistics about the input stage
* @param {Object} summaryContext.mapSummary - Statistics about the map stage
* @param {Object} summaryContext.reduceSummary - Statistics about the reduce stage
* @since 2015.2
*/
const summarize = (summaryContext) => {

}

return {getInputData, map, reduce, summarize}

});

Leave a comment

Your email address will not be published. Required fields are marked *