/**
* @NApiVersion 2.1
* @NScriptType MapReduceScript
* @NModuleScope SameAccount
*/
define(['N/search', 'N/record', './jj_common_library_sbsun762.js'],
/**
* @param{format} format
* @param{record} record
* @param{search} search
* @param{commonLibrary} commonLibrary
*/
(search, record, commonLibrary) => {
/**
* 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 {
let workOrderDetails = getWorkOrderDetails();
log.debug("workOrderDetails",workOrderDetails);
return workOrderDetails;
} catch (e) {
log.error('error@getInputData', e);
}
}
/**
* 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 workOrderObj = JSON.parse(reduceContext.values);
log.debug("workOrderObj",workOrderObj);
let workOrderId = workOrderObj.InternalID.value;
log.debug("workOrderId",workOrderId);
let woCloseRec = record.transform({
fromType: record.Type.WORK_ORDER,
fromId: workOrderId,
toType: record.Type.WORK_ORDER_CLOSE,
isDynamic: true,
});
let id = woCloseRec.save({
enableSourcing: true,
ignoreMandatoryField: true
});
log.debug("transformed to work order close");
}
catch (e) {
log.error('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) => {
// Summarize added to find the Map/Reduce script excution end time.
}
/**
* @description Fetch the work orders where variance is 0 and status is not closed.
* @returns
*/
function getWorkOrderDetails(){
try{
let workOrderSearchObj = search.create({
type: "workorder",
filters:
[
["type","anyof","WorkOrd"],
"AND",
["mainline","is","T"],
"AND",
["formulanumeric: NVL({quantity} - {built}, 0) / NULLIF({quantity}, 0)","equalto","0"],
"AND",
["status","noneof","WorkOrd:H"],
"AND",
["subsidiary","anyof","7","9","2"],
"AND",
["iswip","is","T"]
// "AND",
// [[["internalidnumber","equalto","8269199"]],"OR",[["internalidnumber","equalto","111491374"]],"OR",[["internalidnumber","equalto","111491375"]]]
],
columns:
[
search.createColumn({name: "tranid", label: "DocumentNumber"}),
search.createColumn({name: "internalid", label: "InternalID"}),
search.createColumn({name: "quantity", label: "Quantity"}),
search.createColumn({name: "built", label: "Built"}),
search.createColumn({
name: "formulanumeric",
formula: "NVL({quantity} - {built}, 0) / NULLIF({quantity}, 0)",
label: "Variance"
})
]
});
let searchResultCount = workOrderSearchObj.runPaged().count;
log.debug("workOrderSearchObj result count",searchResultCount);
let workOrderDetailsObj = commonLibrary.iterateSavedSearch(workOrderSearchObj, commonLibrary.fetchSavedSearchColumn(workOrderSearchObj, 'label'));
return workOrderDetailsObj;
}
catch ( Err )
{
log.debug( "Error @getWorkOrderDetails", Err );
log.error( "Error @getWorkOrderDetails", Err )
}
}
return { getInputData, reduce, summarize }
});
/**
* @NApiVersion 2.x
* @NModuleScope SameAccount
*/
/************************************************************************************************
* Description : This library file is used for the common functions
***********************************************************************************************/
define([], () => {
/**
* @description Global variable for storing errors ----> for debugging purposes
* @type {Array.<Error>}Reference
* @constant
*/
const ERROR_STACK = [];
/**
* @description Check whether the given parameter argument has value on it or is it empty.
* ie, To check whether a value exists in parameter
* @param {*} parameter parameter which contains/references some values
* @param {*} parameterName name of the parameter, not mandatory
* @returns {Boolean} true if there exist a value else false
*/
function checkForParameter(parameter, parameterName) {
try {
if (parameter !== "" && parameter !== null && parameter !== undefined && parameter !== false && parameter !== "null" && parameter !== "undefined" && parameter !== " " && parameter !== 'false' && parameter !== '[]' && parameter !== '{}') {
return true;
} else {
if (parameterName) {
return false;
}
}
} catch (e) {
log.error({ title: "error@checkForParameter", details: e });
}
}
/**
* @description To assign a default value if the value argument is empty
* @param {String|Number|Boolean|Object|Array|null|undefined} value
* @param {String|Number|Boolean|Object|Array} defaultValue
* @returns {*} either value or defaultValue
*/
const assignDefaultValue = function assignDefaultValue(value, defaultValue) {
try {
if (checkForParameter(value)) {
return value;
} else {
return defaultValue;
}
} catch (e) {
log.error({ title: "error@assignDefaultValue", details: e });
}
}
/**
* @description To check whether the Given Value is a string or number
* @param value
* @returns {boolean} true if the given value is a string or number , else false
*/
const isNumberOrString = (value) => {
return (util.isString(value) || util.isNumber(value))
};
/**
* @description Common Try-Catch function, applies to Object contains methods/function
* @param {Object.<string,Function|any>} DATA_OBJ Object contains methods/function
* @param {String} NAME Name of the Object
* @returns {void}
*/
const applyTryCatch = function applyTryCatch(DATA_OBJ, NAME) {
/**
* @description Try-Catch function
* @param {Function} myfunction - reference to a function
* @param {String} key - name of the function
* @returns {Function|false}
*/
const tryCatch = function (myfunction, key) {
return function () {
try {
return myfunction.apply(this, arguments);
} catch (e) {
log.error("error in " + key, e);
ERROR_STACK.push(e);
return false;
}
};
}
for (let key in DATA_OBJ) {
if (typeof DATA_OBJ[key] === "function") {
DATA_OBJ[key] = tryCatch(DATA_OBJ[key], NAME + "." + key);
}
}
}
/**
* COnvert the value to fixed decimals or Integer.
* @param {*} value
* @returns
*/
function convertFloat(value, digits) {
value = parseFloat(value);
const formattedResult = value % 1 === 0 ? value.toString() : value.toFixed(digits);
return Number(formattedResult);
}
/**
* @description Object referencing NetSuite Saved Search
* @typedef {Object} SearchObj
* @property {Object[]} filters - Filters Array in Search
* @property {Object[]} columns - Columns Array in Search
*/
/**
* @description to format Saved Search column to key-value pair where each key represents each columns in Saved Search
* @param {SearchObj} savedSearchObj
* @param {void|String} priorityKey
* @returns {Object.<String,SearchObj.columns>}
*/
function fetchSavedSearchColumn(savedSearchObj, priorityKey) {
try {
let columns = savedSearchObj.columns;
let columnsData = {},
columnName = '';
columns.forEach(function (result) {
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;
} catch (e) {
log.error({ title: 'error@fetchSavedSearchColumn', details: e });
}
}
/**
* @description Representing each result in Final Saved Search Format
* @typedef formattedEachSearchResult
* @type {{value:any,text:any}}
*/
/**
* @description to fetch and format the single saved search result. ie, Search result of a single row containing both text and value for each columns
* @param {Object[]} searchResult contains search result of a single row
* @param {Object.<String,SearchObj.columns>} columns
* @returns {Object.<String,formattedEachSearchResult>|{}}
*/
function formatSingleSavedSearchResult(searchResult, columns) {
try {
let responseObj = {};
for (let column in columns) {
responseObj[column] = {
value: searchResult.getValue(columns[column]),
text: searchResult.getText(columns[column])
};
}
return responseObj;
} catch (e) {
log.error({ title: 'error@formatSingleSavedSearchResult', details: e });
}
}
/**
* @description to iterate over and initiate format of each saved search result
* @param {SearchObj} searchObj
* @param {void|Object.<String,SearchObj.columns>} columns
* @returns {[]|Object[]}
*/
function iterateSavedSearch(searchObj, columns) {
try {
if (!checkForParameter(searchObj)) {
return false;
}
if (!checkForParameter(columns)) {
columns = fetchSavedSearchColumn(searchObj);
}
let response = [];
let searchPageRanges;
try {
searchPageRanges = searchObj.runPaged({
pageSize: 1000
});
} catch (err) {
return [];
}
if (searchPageRanges.pageRanges.length < 1) {
return [];
}
let pageRangeLength = searchPageRanges.pageRanges.length;
for (let pageIndex = 0; pageIndex < pageRangeLength; pageIndex++) {
searchPageRanges.fetch({
index: pageIndex
}).data.forEach(function (result) {
response.push(formatSingleSavedSearchResult(result, columns));
});
}
return response;
} catch (e) {
log.error({ title: 'error@iterateSavedSearch', details: e });
}
}
/**
* @description function for handling the escape characters
* @param unsafe Data
* @return {*}
*/
function escapeXml(unsafe) {
try {
if (unsafe) {
return unsafe.replace(/[<>&'"]/g, function (c) {
switch (c) {
case '<':
return '<';
case '>':
return '>';
case '&':
return '&';
case ''':
return ''';
case '"':
return '"';
}
});
}
} catch (e) {
log.error({ title: 'error@escapeXml', details: e });
}
}
return {
checkForParameter,
assignDefaultValue,
isNumberOrString,
applyTryCatch,
convertFloat,
fetchSavedSearchColumn,
iterateSavedSearch,
escapeXml
};
});