NetSuite customers often rely on integrations to seamlessly transfer data both into and out of the system. Various integration platforms-as-a-service (iPaaS) solutions like Boomi and Celigo are commonly employed to achieve this. However, one recurring issue is the occurrence of errors during the data transfer process. Typically, diagnosing these errors involves a time-consuming examination of iPaaS logs on an individual basis to identify the root cause.
A more efficient approach to this challenge is to direct all incoming data to NetSuite and let the platform itself manage the data processing. This enables you to log and monitor errors directly within NetSuite, simplifying error tracking through the use of saved searches. Additionally, customized code within NetSuite can be implemented to proactively reduce or even eliminate these errors altogether. One effective method for this involves creating a ‘Queue’ custom record, which can then be processed using a map/reduce script. In this blog, we’ll guide you through the steps to set up this robust error-handling system within NetSuite. Let’s dive in!
The first list will be a list of “Queue Types”. As an example, I have added “Sales Order” as a queue type. You can have any number of “Queue Types”. You would create one map/reduce script to process each “Queue Type”.
The second list will be a list of statuses for each element of the queue:
Refer to the image below to set the internal IDs of the fields. Your custom record should look like this:
Our queue is going to create sales orders. To accomplish this, set the “Queue Element” record as follows:
This script starts by finding all “Queue Element” records of type “Sales Order” which are in “Queued” or “Working” status. The next retry date must also be before or equal to the time the processor runs. The script then loads the “Queue Element” record. It then processes the payload which in our example creates a sales order. It finally sets the “Queue Element” processed date to now, status to completed, and set the transaction to the sales order that was created.
If an error occurs and we are below the maximum retries (a script parameter), the “Queue Element” record status is set to “Working” and the next retry date is updated. The next retry date will be progressively farther into the future each time it fails to process.
If an error occurs and we are equal to or above the maximum number of retries, the “Queue Element” record status is set to “Failed” and the next retry date is set to empty. The map/reduce will not try to process this record again.
The script deployment can be scheduled to run as needed. The retry logic can be modified to whatever makes sense for your customization.
Add and deploy the script (code shown below). Reference my article “Quick Guide to Adding and Deploying a Script in NetSuite” if needed. You will need to create a script parameter “Number of Maximum Retries” which will be an integer with internal id “custscript_max_retries”.
/**
* @NApiVersion 2.1
* @NScriptType MapReduceScript
* @NModuleScope SameAccount
*/
//------------------------------------------------------------------
//Script: STC_ProcessQueue_MR.js
//------------------------------------------------------------------
define([ 'N/runtime', 'N/record', 'N/search'],
(runtime, record, search) => {
pad = (value) => {
if(value < 10) {
return '0' + value;
} else {
return value;
}
}
getInputData = (context) => {
log.debug('==START==', '==START==');
var now = new Date();
var modifier = now.getUTCHours() > 12 ? 'pm' : 'am';
var hourIn12 = now.getUTCHours() % 12 || 12;
var dateString = (now.getMonth()+1) + '/' + now.getDate() + '/' + now.getFullYear() + ' ' + hourIn12 + ':' + pad(now.getMinutes()) + ' ' + modifier;
var queueElementSearchObj = search.create({
type: "customrecord_stc_queue_element",
filters:
[
["custrecord_stc_queue_element_next_retry", search.Operator.ONORBEFORE, dateString], // only get records where the next retry date is in the past or now
"AND",
["custrecord_stc_queue_element_status","anyof","1","2"], // queued or working
"AND",
["custrecord_stc_queue_element_type","anyof","1"] // sales order
],
columns:
[
'internalid'
]
});
return queueElementSearchObj;
}
reduce = (context) => {
log.debug('context', context);
var queueElementRecord = null;
var error = null;
try {
// load the process request custom record
queueElementRecord = record.load({
type: 'customrecord_stc_queue_element',
id: context.key,
isDynamic: true
});
var payload = queueElementRecord.getValue('custrecord_stc_queue_element_payload');
payload = JSON.parse(payload);
/////////////////////////////////////////////////////////
// CREATE YOUR OWN FUNCTION HERE TO PROCESS THE PAYLOAD
/////////////////////////////////////////////////////////
var salesOrderId = createSalesOrder(payload);
////////////////////////////////////////////////////////
queueElementRecord.setValue('custrecord_stc_queue_element_date_proc', new Date());
queueElementRecord.setValue('custrecord_stc_queue_element_transaction', salesOrderId);
queueElementRecord.setValue('custrecord_stc_queue_element_error', '');
queueElementRecord.setValue('custrecord_stc_queue_element_next_retry', '');
queueElementRecord.setValue('custrecord_stc_queue_element_num_retries', '');
queueElementRecord.setValue('custrecord_stc_queue_element_status', '3'); // completed
queueElementRecord.save();
return;
}
catch(e) {
error = JSON.stringify(e);
log.error('error', error);
}
try {
// if an error occurred and there is a valid queueElementRecord
if (error && queueElementRecord) {
queueElementRecord.setValue('custrecord_stc_queue_element_error', error);
var numRetries = queueElementRecord.getValue('custrecord_stc_queue_element_num_retries');
var scriptObj = runtime.getCurrentScript();
var maxRetries = scriptObj.getParameter({name: 'custscript_max_retries'});
// if we are at maxRetries or greater, mark the record as failed and do not retry
if (numRetries+1 >= maxRetries) {
queueElementRecord.setValue('custrecord_stc_queue_element_status', 4); // failed
queueElementRecord.setValue('custrecord_stc_queue_element_next_retry', '');
}
else {
// otherwise set the the status to working and set next retry date
queueElementRecord.setValue('custrecord_stc_queue_element_status', 2); // working
// Create your own custom retry logic here as needed
// As an example, next retry date is next retry date + numRetries+1 hours
var nextRetryDate = new Date();
var dateToMilliseconds = nextRetryDate.getTime();
var addedHours = dateToMilliseconds + (3600000*(numRetries+1));
var newDate = new Date(addedHours);
queueElementRecord.setValue('custrecord_stc_queue_element_next_retry', newDate);
}
queueElementRecord.setValue('custrecord_stc_queue_element_num_retries', numRetries+1);
queueElementRecord.save();
}
}
catch(e) {
log.error('Error trying to save record in errored state', JSON.stringify(e));
}
}
createSalesOrder = (payload) => {
var salesOrderRecord = record.create({
type: record.Type.SALES_ORDER,
isDynamic: true
});
salesOrderRecord.setValue('entity', payload.customerid);
for (let i = 0; i < payload.items.length; i++) {
var payloadItem = payload.items[i];
var lineNum = salesOrderRecord.selectNewLine({sublistId: 'item'});
salesOrderRecord.setCurrentSublistValue({sublistId: 'item',fieldId: 'item',value: payloadItem.item});
salesOrderRecord.setCurrentSublistValue({sublistId: 'item',fieldId: 'quantity',value: payloadItem.quantity});
salesOrderRecord.setCurrentSublistValue({sublistId: 'item',fieldId: 'rate',value: payloadItem.rate});
salesOrderRecord.commitLine({sublistId: 'item'});
}
var recordId = salesOrderRecord.save();
return recordId;
}
summarize = (summary) => {
log.debug('==END==','==END==');
}
return {
getInputData: getInputData,
reduce: reduce,
summarize: summarize
};
});
After running the map/reduce script you will see the Date Processed, Transaction Created, and the status set to “Completed”.
In order to see what a record looks like in an errored state, let’s create a “Queue Element” record which will error when processed. Create another “Queue Element” record with the same data except set the item internal id to a non-existent item. Then run the map/reduce script again. You will see the error, number of retries, and next retry filled in. The status is set to “Working”. The map/reduce will keep trying to process this record until it is successful or the maximum number of retries is reached. When that happens, the status will be set to “Failed” and there will be no further attempt to process the record.
Backed by its functional features and proven applications, NetSuite can elevate your enterprise process and accelerate business functions to a great extent! One of the main reasons why you should consider NetSuite customization for your business is because it can boost operational efficiency and make your business more process-centric by optimizing your fragmented enterprise actions in just a single cloud platform.
Just like any other robust software solution, NetSuite customization holds the capacity to address and solve your business pain point – be it a small problem or a complex issue! A reliable and reputed team of NetSuite consultants like us can help you come up with the best strategies and plan to make ERP unleash its full potential to solve your specific business problems.
The design, implementation, and customization strategies of NetSuite might be developed solely on the basis of the specific industry an organization is dealing with. This is dependent on the operational process and size, which comply with every business operation.
Every company has its own unique and distinct business processes, workflows, personalized software needs, and the likes. One of the most remarkable perks of NetSuite is that it’s extremely customizable. If your company requirements are unique or specific, you require trusted NetSuite consultants who hold extensive experience and expertise with robust software development and architecture for making the solution suitable to your requirements.
At Suite Tooth Consulting, we emphasize building a full-fledged strategy to ideate on a plan on action right from the inception until the “go-live” as well as “post-implementation” rescue timetable. Implementing and customizing a robust ERP like NetSuite is a complex and detailed procedure. It can potentially entail a few days or even a few months for more complex builds. As your top-trusted team of consultants, we shall provide you with a detailed forecast or a breakdown of timeline to give you a heads-up on how long our team can potentially take to wrap the entire process.
We aim to bring unmatched expertise and professionalism to your NetSuite initiatives. Let’s talk about how our NetSuite consultancy can make a difference!