We have two functions that invoke the same Map/Reduce script, depending on the ongoing deployments.
Additionally, we are dynamically passing parameters, including filters for the Saved Search, from each function to the Map/Reduce script.
The following function creates the Map/Reduce script if the same deployment is not currently active.
It applies filters for serialized and lot-numbered inventory items , File in File Cabinet where the data is stored and passes them as parameters.
refreshInventoryItemsCache() {
try {
let scheduledscriptinstanceSearchObj = search.create({
type: "scheduledscriptinstance",
filters:
[
["scriptdeployment.scriptid", "is", "customdeploy_jj_mr_2"],
"AND",
["mapreducestage", "anyof", "SUMMARIZE"],
"AND",
["status", "anyof", "PENDING", "PROCESSING"]
],
columns:
[
search.createColumn({ name: "datecreated", label: "Date Created" }),
search.createColumn({ name: "startdate", label: "Start Date" }),
search.createColumn({ name: "enddate", label: "End Date" }),
search.createColumn({ name: "status", label: "Status" }),
search.createColumn({ name: "mapreducestage", label: "Map/Reduce Stage" }),
search.createColumn({ name: "percentcomplete", label: "Percent Complete" })
]
});
let searchResultCount = scheduledscriptinstanceSearchObj.runPaged().count;
log.debug("scheduledscriptinstanceSearchObj result count", searchResultCount);
const filter = [
["isserialitem", "is", "T"], "OR", ["islotitem", "is", "T"],
"AND",
["type", "anyof", "InvtPart"],
"AND",
["isinactive", "is", "F"]
];
if (searchResultCount == 0) {
// Submit the Map/Reduce task to refresh the cache asynchronously
let mrTask = task.create({
taskType: task.TaskType.MAP_REDUCE,
scriptId: 'customscript_jj_mr_ref_asmbly_item_cache', // Single Map/Reduce script ID
deploymentId: 'customdeploy_jj_ref_inv_items_cache', // Deployment ID
params: {
custscript_jj_item_search_filter: JSON.stringify(filter), // Pass filter as a parameter
custscript_jj_item_file_name: 'InvItemsCache.json', // Cache file name
}
});
let taskId = mrTask.submit();
log.debug('Map/Reduce Task Submitted', 'Task ID: ' + taskId);
return { status: 'SUCCESS', reason: 'Cache refresh has started', data: [] };
} else {
return { status: 'SUCCESS', reason: 'Cache refresh is currently in progress', data: [] };
}
} catch (e) {
log.error('error @ refreshItemCache', e.message);
return { status: 'ERROR', reason: 'Failed to Refresh Inv Item Cache: ' + e.message, data: [] };
}
},
The following function creates the Map/Reduce script if another deployment is not currently active for the same Script.
It applies filters for Assembly items, File in File Cabinet where the data is stored and passes them as parameters.
refreshAssemblyItemsCache() {
try {
let scheduledscriptinstanceSearchObj = search.create({
type: "scheduledscriptinstance",
filters:
[
["scriptdeployment.scriptid", "is", "customdeploy_jj_mr_1"],
"AND",
["mapreducestage", "anyof", "SUMMARIZE"],
"AND",
["status", "anyof", "PENDING", "PROCESSING"]
],
columns:
[
search.createColumn({ name: "datecreated", label: "Date Created" }),
search.createColumn({ name: "startdate", label: "Start Date" }),
search.createColumn({ name: "enddate", label: "End Date" }),
search.createColumn({ name: "status", label: "Status" }),
search.createColumn({ name: "mapreducestage", label: "Map/Reduce Stage" }),
search.createColumn({ name: "percentcomplete", label: "Percent Complete" })
]
});
let searchResultCount = scheduledscriptinstanceSearchObj.runPaged().count;
log.debug("scheduledscriptinstanceSearchObj result count", searchResultCount);
if (searchResultCount == 0) {
const filter = [
['isinactive', 'is', 'F'],
"AND",
["type", "anyof", "Assembly"]
];
let mrTask = task.create({
taskType: task.TaskType.MAP_REDUCE,
scriptId: 'customscript_jj_mr_ref_asmbly_item_cache', // Single Map/Reduce script ID
deploymentId: 'customdeploy_jj_mr_ref_asmbly_item_cache', // Deployment ID
params: {
custscript_jj_item_search_filter: JSON.stringify(filter), // Pass filter as a parameter
custscript_jj_item_file_name: 'AssemblyItemCache.json', // Cache file name
}
});
let taskId = mrTask.submit();
log.debug('Map/Reduce Task Submitted', 'Task ID: ' + taskId);
return { status: 'SUCCESS', reason: 'Cache refresh has started', data: [] };
} else {
return { status: 'SUCCESS', reason: 'Cache refresh is currently in progress', data: [] };
}
} catch (e) {
log.error('Error in refreshAssemblyItemsCache', e);
return { status: 'ERROR', reason: 'Refreshing Assembly Item Cache Failed: ' + e.message };
}
},
The following is the Map/Reduce script, where two separate deployments have been created, each with parameters configured through the UI.
**
* @NApiVersion 2.1
* @NScriptType MapReduceScript
*/
define(['N/search', 'N/file', 'N/log', 'N/runtime'], (search, file, log, runtime) => {
const getInputData = () => {
try {
let items = [];
const script = runtime.getCurrentScript();
var searchFilterParam = runtime.getCurrentScript().getParameter({ name: 'custscript_jj_item_search_filter' });
var filter = searchFilterParam ? JSON.parse(searchFilterParam) : [];
log.debug('Filter', filter);
const itemSearch = search.create({
type: search.Type.ITEM, // General item type for flexibility
filters: filter,
columns: ['internalid', 'name']
});
let pagedData = itemSearch.runPaged({ pageSize: 1000 });
pagedData.pageRanges.forEach(pageRange => {
let page = pagedData.fetch({ index: pageRange.index });
page.data.forEach(result => {
items.push({
id: result.getValue('internalid'),
name: result.getValue('name')
});
});
});
log.debug('getInputData', `Fetched ${items.length} items.`);
return items;
} catch (error) {
log.error('Error in getInputData', error);
}
};
const map = (context) => {
try {
const item = JSON.parse(context.value);
context.write(item.id, context.value); // Key: Item ID, Value: Item JSON
} catch (error) {
log.error('Error in map', error);
}
};
const summarize = (summaryContext) => {
try {
const script = runtime.getCurrentScript();
const fileName = script.getParameter({ name: 'custscript_jj_item_file_name' })
let allItems = [];
summaryContext.output.iterator().each((key, value) => {
allItems.push(JSON.parse(value));
return true;
});
log.debug('summarize', `Total items to write: ${allItems.length}`);
const dataToStore = encodeURIComponent(JSON.stringify(allItems));
// Create or overwrite the file in the file cabinet
const fileObj = file.create({
name: fileName,
fileType: file.Type.JSON,
contents: dataToStore,
folder: 1432,
isOnline: true //if needed to fetch externally.
});
let fileId = fileObj.save();
log.audit('summarize', ` file created/updated: ${fileId} `);
} catch (error) {
log.error('Error in summarize', error);
}
};
return {
getInputData,
map,
summarize
};
});
By invoking the two functions, the Map/Reduce script can be executed. This process ensures that the file in the File Cabinet is either updated or created as needed.