Jira Code: BPL-6, BPL-7
Dependency : moment.js
This will calculate the ATS (Available to Sell) quantity of items by considering all the Sales Order, Purchase Order, Current Inventory Quantity at each location for each bucket period.
Bucket Period is considered as the time between each successive Purchase Order.
ATS is similar to NetSuite ‘Available To Promise’ feature.
There is a Suitelet form which can be used to check the item ATS details for each item.
The Scheduled Script is used to calculate the ATS value for all the items under consideration.
There is a custom record which will store the BrandScope Credentials and Endpoints. This custom records will also store the Item Fields Names, Sales Order and Purchase Order Date fields which should be taken into consideration for the ATS Calculation.
There are custom records which will store the location and brand of items that should be taken under consideration for the scheduler.
The scheduler will calculate the ATS quantity for each item and store this in a custom record and after every item is processed it will be sent to BrandScope.
BPL-7 JJ BrandScope Logic.js (Custom Module)
/**
* @NApiVersion 2.x
* @NModuleScope SameAccount
*/
/*******************************************************************************
* * Jobin & Jismi IT Services | BrandScope | Logic *
* **************************************************************************
*
* Author: Jobin & Jismi IT Services LLP
*
* Date Created : 22-March-2019
*
* Created By: Manu Antony, Jobin & Jismi IT Services LLP
*
* REVISION HISTORY
*
*
******************************************************************************/
"use strict";
var BRANDSCOPE_SETUP = {
brandscope_fields: {
atstoken: "",
atsendpoint: "",
nsemployee: "-5"
},
item_fields: {
sku: "internalid",
color: "internalid",
size: "internalid",
brand: "internalid"
},
salesorder_fields: {
date: "trandate"
},
purchaseorder_fields: {
date: "duedate"
},
date_format: {
ns_format: "",
required_format: "DD/MM/YYYY"
}
};
var PATH = {
basePath: "/SuiteScripts/JJ BrandScope Dependencies/",
suiteBundlePath: "/SuiteBundles/Bundle 274844/JJ BrandScope Dependencies/"
};
define(["N/search", 'N/config', "N/util", "N/log", './moment'],
function (search, config, util, log, moment) {
//Defining logger, to know log comes from this module
var LOG = {
debug: function (title, details) {
log.debug("MODULE (BPL-7 JJ BrandScope Logic) : " + title, details);
},
error: function (title, details) {
log.error("MODULE (BPL-7 JJ BrandScope Logic) : " + title, details);
}
};
//To check whether a value exists in parameter
function checkForParameter(parameter, parameterName) {
if (parameter !== null && parameter !== undefined && parameter !== false && parameter !== "null" && parameter !== "undefined" && parameter !== "false" && parameter !== "" && parameter !== " ")
return true;
if (parameterName)
LOG.debug("Empty Value found", "Empty Value for parameter " + parameterName);
return false;
}
//To assign a default value if the it is empty
function assignDefaultValue(value, defaultValue) {
if (checkForParameter(value)) return value;
return defaultValue;
}
//To reject predefined set of values
function rejectThisValues(value) {
var rejectObj = {
null: true,
undefined: true,
NaN: true,
0: true,
false: true,
"": true
};
return rejectObj[value] ? false : true;
}
//Common Try-Catch function
function applyTryCatch(DATA_OBJ, NAME) {
function tryCatch(myfunction, key) {
return function () {
try {
return myfunction.apply(this, arguments);
} catch (e) {
LOG.error("error in " + key, e);
return false;
}
};
}
for (var key in DATA_OBJ) {
if (typeof DATA_OBJ[key] === "function") {
DATA_OBJ[key] = tryCatch(DATA_OBJ[key], NAME + "." + key);
}
}
}
//Date Manipulation Logic using the functions from third party library moment.js
var dateLogic = {
isBetween: function (startDate, endDate, compareDate, type, edgecase) {
/* edgecase are :
'()' - default exclusive
'(]' - right inclusive
'[)' - left inclusive
'[]' - all inclusive
*/
edgecase = assignDefaultValue(edgecase, "()").toString().trim();
type = assignDefaultValue(type, "days").toString().trim();
//If no CompareDate is given
if (!checkForParameter(compareDate)) return false;
//If no startDate or endDate are given
if (!checkForParameter(startDate) && !checkForParameter(endDate))
return false;
startDate = startDate ? dateLogic.formatDateAsMoment(startDate) : false;
endDate = endDate ? dateLogic.formatDateAsMoment(endDate) : false;
compareDate = dateLogic.formatDateAsMoment(compareDate);
//If there is StartDate but no EndDate is given
if (checkForParameter(startDate) && !checkForParameter(endDate)) {
if (edgecase === "[]" || edgecase === "[)")
if (compareDate.isSame(startDate)) return true;
if (compareDate.isAfter(startDate)) return true;
return false;
}
//If there is EndDate but no StartDate is given
if (!checkForParameter(startDate) && checkForParameter(endDate)) {
if (edgecase === "[]" || edgecase === "(]")
if (compareDate.isSame(endDate)) return true;
if (compareDate.isBefore(endDate)) return true;
return false;
}
return compareDate.isBetween(startDate, endDate, type, edgecase);
},
formatDateAsMoment: function (date) {
//returns date as moment object
return moment(date, BRANDSCOPE_SETUP.date_format.ns_format);
},
formatDate: function (date, type) {
//returns date as specified string format
return moment(date, BRANDSCOPE_SETUP.date_format.ns_format).format(type ? type : BRANDSCOPE_SETUP.date_format.ns_format);
}
};
applyTryCatch(dateLogic, "dateLogic");
//dataSets from saved Saved Search and formating Saved Search response
var dataSets = {
formatSavedSearch: function (savedSearchObj, savedSearchName, mergeObj, sendAsObjectIfSingleResult, prioritizeSingleValue) {
//formating saved search result
//Creating Column Names
var columns = savedSearchObj.columns;
var columnsData = {};
columns.forEach(function (result, counter) {
columnsData[result.label] = result;
});
//Paginating Results
var searchPageRanges;
try {
searchPageRanges = savedSearchObj.runPaged({
pageSize: 1000
});
} catch (err) {
return mergeObj ? mergeObj : [];
}
if (searchPageRanges.pageRanges.length < 1)
return mergeObj ? mergeObj : [];
//Fetching Row Data
var rowData = [],
tempRow = {};
for (var pageIndex = 0; pageIndex < searchPageRanges.pageRanges.length; pageIndex++)
searchPageRanges.fetch({
index: pageIndex
}).data
.forEach(function (result) {
tempRow = undefined;
tempRow = {};
if (prioritizeSingleValue)
for (var key in columnsData)
tempRow[key] = result.getText(columnsData[key]) ? result.getText(columnsData[key]) : result.getValue(columnsData[key]);
else
for (var key in columnsData)
tempRow[key] = {
value: result.getValue(columnsData[key]),
text: result.getText(columnsData[key])
};
if (mergeObj)
if (util.isObject(mergeObj)) Object.assign(tempRow, mergeObj);
rowData.push(tempRow);
});
if (sendAsObjectIfSingleResult)
if (rowData.length === 1) return rowData[0];
return rowData;
},
brandScopeSetup: function () {
//searching BrandScope Setup
var brandScopeSetupSearch = search.create({
type: "customrecord_jj_bpl2_cust_brandsetup",
filters: [
["isinactive", "is", "F"]
],
columns: [
search.createColumn({
name: "custrecord_jj_bpl2_ats_token",
label: "custrecord_jj_bpl2_ats_token" //"ATS TOKEN"
}),
search.createColumn({
name: "custrecord_jj_bpl2_ata_endpoint",
label: "custrecord_jj_bpl2_ata_endpoint" // "ATS ENDPOINT"
}),
search.createColumn({
name: "custrecord_jj_bpl2_email_sender",
label: "custrecord_jj_bpl2_email_sender" // "EMAIL SENDER(EMPLOYEE)"
}),
search.createColumn({
name: "custrecord_jj_bpl2_integration_field_id",
label: "custrecord_jj_bpl2_integration_field_id" // "INTEGRATION FIELD ID"
}),
search.createColumn({
name: "custrecord_jj_bpl2_item_color_fieldid",
label: "custrecord_jj_bpl2_item_color_fieldid" // "ITEM COLOUR FIELD ID"
}),
search.createColumn({
name: "custrecord_jj_bpl2_item_size_fieldid",
label: "custrecord_jj_bpl2_item_size_fieldid" // "ITEM SIZE FIELD ID"
}),
search.createColumn({
name: "custrecord_jj_bpl2_item_brand_fieldid",
label: "custrecord_jj_bpl2_item_brand_fieldid" // "ITEM BRAND FIELD ID"
}),
search.createColumn({
name: "custrecord_jj_bpl2_expected_ship_date",
label: "custrecord_jj_bpl2_expected_ship_date" // "EXPECTED SHIP DATE FIELD ID"
}),
search.createColumn({
name: "custrecord_jj_bpl2_expectd_rec_ship_date",
label: "custrecord_jj_bpl2_expectd_rec_ship_date" // "EXPECTED RECEIVED SHIP DATE FIELD ID"
})
]
});
var searchResultCount = brandScopeSetupSearch.runPaged().count;
LOG.debug("brandScopeSetupSearch result count", searchResultCount);
return dataSets.formatSavedSearch(brandScopeSetupSearch, "brandScopeSetupSearch");
},
brandScopeLocation: function () {
//searching BrandScope Location
var brandScopeLocationSearch = search.create({
type: "customrecord_jj_bpl2_brandscope_location",
filters: [
["isinactive", "is", "F"]
],
columns: [
search.createColumn({
name: "custrecord_jj_bpl2_location",
sort: search.Sort.ASC,
label: "Location"
}),
search.createColumn({
name: "custrecord_jj_bpl2_abbreviations",
label: "Abbreviations"
})
]
});
var searchResultCount = brandScopeLocationSearch.runPaged().count;
LOG.debug("brandScopeLocationSearch result count", searchResultCount);
return dataSets.formatSavedSearch(brandScopeLocationSearch, "brandScopeLocationSearch");
},
brandScopeBrand: function () {
//searching BrandScope Brand
var brandScopeBrandSearch = search.create({
type: "customrecord_jj_bpl2_custbrandid",
filters: [
["isinactive", "is", "F"]
],
columns: [
search.createColumn({
name: "custrecord_jj_bpl2_brandlocation",
sort: search.Sort.ASC,
label: "Location"
}),
search.createColumn({
name: "custrecord_jj_bpl2_brand",
label: "Brand"
})
]
});
var searchResultCount = brandScopeBrandSearch.runPaged().count;
LOG.debug("brandScopeBrandSearch result count", searchResultCount);
return dataSets.formatSavedSearch(brandScopeBrandSearch, "brandScopeBrandSearch");
},
brandScopeSchedulerDataSet: function () {
//searching BrandScope Scheduler DateSet
var brandScopeSchedulerDataSetSearch = search.create({
type: "customrecord_jj_bpl7_brandscope_schedule",
filters: [],
columns: [
search.createColumn({
name: "internalid",
sort: search.Sort.ASC,
label: "Internal ID"
}),
search.createColumn({
name: "custrecord_bpl7_scheduler_item",
label: "Item"
}),
search.createColumn({
name: "custrecord_bpl7_scheduler_data",
label: "Data"
})
]
});
var searchResultCount = brandScopeSchedulerDataSetSearch.runPaged().count;
LOG.debug("brandScopeSchedulerDataSetSearch result count", searchResultCount);
return dataSets.formatSavedSearch(brandScopeSchedulerDataSetSearch, "brandScopeSchedulerDataSetSearch");
},
approvedItems: function (INVENTORY_LOCATION, BRANDS) {
//searching Items by Inventory Location and Brand
var approvedItemsSearch = search.create({
type: "item",
filters: [
["custitem_jj_includebrandscope", "is", "T"], //Include In Brandscope Sync
"AND",
["inventorylocation", "anyof", INVENTORY_LOCATION], //Inventory Location
"AND",
[BRANDSCOPE_SETUP.item_fields.brand, "anyof", BRANDS], //Brandfield from BranScope Setup
"AND",
["isinactive", "is", "F"] //Inactive
],
columns: [
search.createColumn({
name: "internalid",
sort: search.Sort.ASC,
label: "Internal ID"
}),
search.createColumn({
name: "itemid",
label: "Name"
}),
search.createColumn({
name: "inventorylocation",
label: "Inventory Location"
}),
search.createColumn({
name: "formulatext",
formula: "'ATS'",
label: "Type"
}),
search.createColumn({
name: "formulatext",
formula: "'NetSuite'",
label: "SourceSystem"
}),
search.createColumn({
name: BRANDSCOPE_SETUP.item_fields.brand,
label: "Brand"
}),
search.createColumn({
name: BRANDSCOPE_SETUP.item_fields.sku,
label: "Integration_ID"
}),
search.createColumn({
name: "upccode",
label: "Barcode"
}),
search.createColumn({
name: "itemid",
label: "ProductName"
}),
search.createColumn({
name: BRANDSCOPE_SETUP.item_fields.color,
label: "Color"
}),
search.createColumn({
name: BRANDSCOPE_SETUP.item_fields.size,
label: "Size"
})
]
});
var searchResultCount = approvedItemsSearch.runPaged().count;
LOG.debug("approvedItemsSearch result count", searchResultCount);
return dataSets.formatSavedSearch(approvedItemsSearch, "approvedItemsSearch");
},
item: function (ITEM_ID, LOCATION_ID, dataObj) {
//searching Items by Item Id and Inventory Location
var itemSearchObj = search.create({
type: "item",
filters: [
["internalid", "is", ITEM_ID.toString().trim()],
"AND",
["inventorylocation", "anyof", LOCATION_ID.toString().trim()]
],
columns: [
search.createColumn({
name: "internalid",
sort: search.Sort.ASC,
label: "_internalid"
}),
search.createColumn({
name: "itemid",
label: "_itemid"
}),
search.createColumn({
name: "displayname",
label: "_displayname"
}),
search.createColumn({
name: "upccode",
label: "_itemfield"
}),
search.createColumn({
name: BRANDSCOPE_SETUP.item_fields.sku,
label: "_sku"
}),
search.createColumn({
name: BRANDSCOPE_SETUP.item_fields.color,
label: "_color"
}),
search.createColumn({
name: BRANDSCOPE_SETUP.item_fields.size,
label: "_size"
}),
search.createColumn({
name: BRANDSCOPE_SETUP.item_fields.brand,
label: "_brand"
}),
search.createColumn({
name: "inventorylocation",
label: "_locationfield"
}),
search.createColumn({
name: "location",
label: "_location"
}),
search.createColumn({
name: "locationquantityavailable",
label: "_totalqtyavailable"
}),
search.createColumn({
name: "locationquantitybackordered",
label: "_totalqtybackordered"
}),
search.createColumn({
name: "locationquantitycommitted",
label: "_totalqtycommitted"
}),
search.createColumn({
name: "locationquantityonhand",
label: "_totalqtyonhand"
}),
search.createColumn({
name: "locationquantityonorder",
label: "_totalqtyonorder"
})
]
});
var searchResultCount = itemSearchObj.runPaged().count;
LOG.debug("itemSearchObj result count", searchResultCount);
return dataSets.formatSavedSearch(itemSearchObj, "itemSearchObj", dataObj, true, true);
},
purchaseOrder: function (ITEM_ID, LOCATION_ID) {
//searching Purchase Order by Item Id and Inventory Location
var DATE_FIELD = assignDefaultValue(BRANDSCOPE_SETUP.purchaseorder_fields.date, "duedate");
var purchaseorderSearchObj = search.create({
type: "purchaseorder",
filters: [
["type", "anyof", "PurchOrd"], //Type is Purchase Order
"AND",
["status", "anyof", "PurchOrd:D", "PurchOrd:E", "PurchOrd:B"], //Status is any of Purchase Order:Partially Received, Purchase Order:Pending Billing/Partially Received, Purchase Order:Pending Receipt
"AND",
["mainline", "is", "F"], //Main Line is false
"AND",
["location", "anyof", LOCATION_ID.toString().trim()],
"AND",
["item", "anyof", ITEM_ID.toString().trim()]
],
columns: [
search.createColumn({
name: "internalid",
summary: "GROUP",
label: "internalid" //Internal ID
}),
search.createColumn({
name: "tranid",
summary: "GROUP",
label: "tranid" //Document Number
}),
search.createColumn({
name: "statusref",
summary: "MAX",
label: "status" //Status
}),
search.createColumn({
name: "item",
summary: "MAX",
label: "item" //Item
}),
search.createColumn({
name: "trandate",
summary: "MAX",
label: "trandate" //Date
}),
search.createColumn({
name: "duedate",
summary: "MAX",
label: "duedate" // Due Date/Receive By
}),
search.createColumn({
name: DATE_FIELD,
summary: "MAX",
sort: search.Sort.ASC,
label: "DATE_FIELD" //Date Field mentioned in BrandScope Setup
}),
search.createColumn({
name: "location",
summary: "GROUP",
label: "location" //Location
}),
search.createColumn({
name: "quantity",
summary: "SUM",
label: "quantity" //Quantity
}),
search.createColumn({
name: "quantityshiprecv",
summary: "SUM",
label: "quantityshiprecv" //Quantity Fulfilled/Received
}),
search.createColumn({
name: "formulanumeric",
summary: "SUM",
formula: "{quantity}-nvl({quantityshiprecv},0)",
label: "quantityremaining" //Quantity Remaining
})
]
});
var searchResultCount = purchaseorderSearchObj.runPaged().count;
LOG.debug("purchaseorderSearchObj result count", searchResultCount);
return dataSets.formatSavedSearch(purchaseorderSearchObj, "purchaseorderSearchObj");
},
salesOrder: function (ITEM_ID, LOCATION_ID, FIRST_PURCHASE_ORDER_DATE) {
//searching Sales Order by Item Id and Inventory Location
var DATE_FIELD = assignDefaultValue(BRANDSCOPE_SETUP.salesorder_fields.date, "trandate").toString().trim();
if (FIRST_PURCHASE_ORDER_DATE)
FIRST_PURCHASE_ORDER_DATE = moment(FIRST_PURCHASE_ORDER_DATE, BRANDSCOPE_SETUP.date_format.ns_format).format('MM/DD/YYYY');
var QTY_REMAINING_FORMULA = '';
if (FIRST_PURCHASE_ORDER_DATE)
QTY_REMAINING_FORMULA = "CASE WHEN {" + DATE_FIELD + "}<TO_DATE('" + FIRST_PURCHASE_ORDER_DATE + "','MM/DD/YYYY') THEN (CASE WHEN(nvl({quantitycommitted}, 0) + nvl({quantityshiprecv}, 0) + {quantity} - nvl({quantityshiprecv}, 0) - nvl({quantitycommitted}, 0)) = 0 THEN {quantity} ELSE({quantity} - nvl({quantityshiprecv}, 0) - nvl({quantitycommitted}, 0)) END) ELSE ({quantity}-nvl({quantityshiprecv}, 0)) END"; //IF(DATE_FIELD<FIRST_PURCHASE_ORDER_DATE) THEN (IF (COMITTED + BACKORDERD + FULFILLED = 0) THEN QUANTITY ELSE BACKORDERD) ELSE (QUANTITY - FULFILLED)
else
QTY_REMAINING_FORMULA = "CASE WHEN (nvl({quantitycommitted},0)+nvl({quantityshiprecv},0)+{quantity}-nvl({quantityshiprecv},0)-nvl({quantitycommitted},0))=0 THEN {quantity} ELSE ({quantity}-nvl({quantityshiprecv},0)-nvl({quantitycommitted},0)) END"; //IF COMITTED + BACKORDERD + FULFILLED = 0 , THEN QUANTITY ELSE BACKORDERD
var salesorderSearchObj = search.create({
type: "salesorder",
filters: [
["type", "anyof", "SalesOrd"], //Type is Sales Order
"AND",
["status", "anyof", "SalesOrd:D", "SalesOrd:A", "SalesOrd:E", "SalesOrd:B"], //Status is any of Sales Order:Partially Fulfilled, Sales Order:Pending Approval, Sales Order:Pending Billing/Partially Fulfilled, Sales Order:Pending Fulfillment
"AND",
["mainline", "is", "F"], //Main Line is false
"AND",
["location", "anyof", LOCATION_ID.toString().trim()],
"AND",
["item", "anyof", ITEM_ID.toString().trim()]
],
columns: [
search.createColumn({
name: "internalid",
summary: "GROUP",
label: "internalid" //Internal ID
}),
search.createColumn({
name: "tranid",
summary: "GROUP",
label: "tranid" //Document Number
}),
search.createColumn({
name: "statusref",
summary: "MAX",
label: "status" //Status
}),
search.createColumn({
name: "item",
summary: "MAX",
label: "item" //Item
}),
search.createColumn({
name: "trandate",
summary: "MAX",
label: "trandate" //Date
}),
search.createColumn({
name: DATE_FIELD,
summary: "MAX",
sort: search.Sort.ASC,
label: "DATE_FIELD" //Date Field mentioned in BrandScope Setup
}),
search.createColumn({
name: "location",
summary: "GROUP",
label: "location" //Location
}),
search.createColumn({
name: "quantity",
summary: "SUM",
label: "quantity" //Quantity
}),
search.createColumn({
name: "quantityshiprecv",
summary: "SUM",
label: "quantityshiprecv" //Quantity Fulfilled/Received
}),
search.createColumn({
name: "quantitycommitted",
summary: "SUM",
label: "quantitycommitted" //Quantity Committed
}),
search.createColumn({
name: "formulanumeric",
summary: "SUM",
formula: " {quantity}-nvl({quantityshiprecv},0)-nvl({quantitycommitted},0)",
label: "quantitybackordered" //Quantity Backordered
}),
search.createColumn({
name: "formulanumeric",
summary: "SUM",
formula: QTY_REMAINING_FORMULA,
label: "quantityremaining" //Quantity Remaining With OffSet
})
]
});
var searchResultCount = salesorderSearchObj.runPaged().count;
LOG.debug("salesorderSearchObj result count", searchResultCount);
return dataSets.formatSavedSearch(salesorderSearchObj, "salesorderSearchObj");
}
};
applyTryCatch(dataSets, "dataSets");
//Manipulating and formating Dataset
var dataManipulate = {
initialiseBrandScopeSetup: function (brandScopeSetup) {
//Initialise BrandScope environment by fetching NetSuite Field Id's and Date Format
//USER_PREFERENCES
var USER_PREFERENCES = config.load({
type: config.Type.USER_PREFERENCES
});
var USER_PREFERENCES_DATEFORMAT = USER_PREFERENCES.getValue({
fieldId: 'DATEFORMAT'
});
USER_PREFERENCES_DATEFORMAT = USER_PREFERENCES_DATEFORMAT.toString().trim().replace(/fm/gi, "").replace(/fd/gi, "").replace(/fy/gi, "").replace(/fn/gi, "");
//COMPANY_PREFERENCES
var COMPANY_PREFERENCES = config.load({
type: config.Type.COMPANY_PREFERENCES
});
var COMPANY_PREFERENCES_DATEFORMAT = COMPANY_PREFERENCES.getValue({
fieldId: 'DATEFORMAT'
});
COMPANY_PREFERENCES_DATEFORMAT = COMPANY_PREFERENCES_DATEFORMAT.toString().trim().replace(/fm/gi, "").replace(/fd/gi, "").replace(/fy/gi, "").replace(/fn/gi, "");
BRANDSCOPE_SETUP = {
brandscope_fields: {
atstoken: assignDefaultValue(brandScopeSetup.custrecord_jj_bpl2_ats_token.value, ""),
atsendpoint: assignDefaultValue(brandScopeSetup.custrecord_jj_bpl2_ata_endpoint.value, ""),
nsemployee: assignDefaultValue(brandScopeSetup.custrecord_jj_bpl2_email_sender.value, "-5")
},
item_fields: {
sku: assignDefaultValue(brandScopeSetup.custrecord_jj_bpl2_integration_field_id.value, "internalid"),
color: assignDefaultValue(brandScopeSetup.custrecord_jj_bpl2_item_color_fieldid.value, "internalid"),
size: assignDefaultValue(brandScopeSetup.custrecord_jj_bpl2_item_size_fieldid.value, "internalid"),
brand: assignDefaultValue(brandScopeSetup.custrecord_jj_bpl2_item_brand_fieldid.value, "internalid"),
},
salesorder_fields: {
date: assignDefaultValue(brandScopeSetup.custrecord_jj_bpl2_expected_ship_date.value, "trandate")
},
purchaseorder_fields: {
date: assignDefaultValue(brandScopeSetup.custrecord_jj_bpl2_expectd_rec_ship_date.value, "duedate")
},
date_format: {
ns_format: assignDefaultValue(COMPANY_PREFERENCES_DATEFORMAT),
required_format: "DD/MM/YYYY"
}
};
return true;
},
findFirstPurchaseOrderDate: function (dataObj, PO_ARRAY) {
//returns first future dated purchase order date
var FIRST_PURCHASE_ORDER_DATE = false;
for (var index = 0, len = PO_ARRAY.length; index < len; index++) {
if (dateLogic.isBetween(dataObj._atoncedate, false, PO_ARRAY[index].DATE_FIELD.value, "days", "()")) { //Future Dated Purchase Order created after today
FIRST_PURCHASE_ORDER_DATE = PO_ARRAY[index].DATE_FIELD.value;
break;
}
}
return FIRST_PURCHASE_ORDER_DATE;
},
findOffset: function (dataObj, SO_ARRAY, PO_ARRAY) {
//calculate OffSet Qunatity
if (SO_ARRAY.length < 1) return "0";
var FIRST_PURCHASE_ORDER_DATE = PO_ARRAY.length > 0 ? dataManipulate.findFirstPurchaseOrderDate(dataObj, PO_ARRAY) : false;
if (!FIRST_PURCHASE_ORDER_DATE) return "0";
var OFFSET = 0;
for (var index = 0, len = SO_ARRAY.length; index < len; index++) {
//if no value is given for the date field, skip that iteration
if (!checkForParameter(SO_ARRAY[index].DATE_FIELD.value)) continue;
if (dateLogic.isBetween(FIRST_PURCHASE_ORDER_DATE, false, SO_ARRAY[index].DATE_FIELD.value, "days", "[)")) //Offset is Calulated by taking All Sales Order created on or after first future dated purchase order
OFFSET += Number(assignDefaultValue(SO_ARRAY[index].quantitycommitted.value, 0));
}
return OFFSET.toString();
},
findUnreceivedPastPurchaseOrder: function (dataObj, PO_ARRAY) {
//Calculate the remaining qunaity of past dated (and present) pending Purchase Order
if (PO_ARRAY.length < 1) return Number(0).toString();
var filteredArray = PO_ARRAY.filter(function (result) {
return dateLogic.isBetween(false, dataObj._atoncedate, result.DATE_FIELD.value, "days", "(]"); //Past Dated Purchase order including today
});
if (filteredArray.length > 0)
return assignDefaultValue(filteredArray.reduce(function (accumulator, currentValue) {
return (Number(accumulator) + Number(currentValue.quantityremaining.value));
}, 0), 0).toString();
return Number(0).toString();
},
createSortedDateArray: function (dataObj, PO_ARRAY) {
//creates Sorted Date Array
var DATE_ARRAY = [],
skipDuplicateDate = {};
DATE_ARRAY.push(dataObj._atoncedate);
skipDuplicateDate[dataObj._atoncedate] = true;
for (var index = 0, len = PO_ARRAY.length; index < len; index++)
if (dateLogic.isBetween(dataObj._atoncedate, false, PO_ARRAY[index].DATE_FIELD.value, "days", "()")) //Skip all past dated Purchase Order including today
if (!skipDuplicateDate[PO_ARRAY[index].DATE_FIELD.value]) {
DATE_ARRAY.push(PO_ARRAY[index].DATE_FIELD.value);
skipDuplicateDate[PO_ARRAY[index].DATE_FIELD.value] = true;
}
return DATE_ARRAY;
},
createBucket: function (dataObj, SO_ARRAY, PO_ARRAY) {
//Create periods as Bucket and assigns value into each Bucket Array
var DATE_ARRAY = dataManipulate.createSortedDateArray(dataObj, PO_ARRAY);
var BUCKET = {};
for (var dateIndex = 0, dateLen = DATE_ARRAY.length; dateIndex < dateLen; dateIndex++) {
BUCKET[DATE_ARRAY[dateIndex]] = [];
for (var soIndex = 0, soLen = SO_ARRAY.length; soIndex < soLen; soIndex++) {
if (dateIndex == 0) { //if date is start date or atonce date
if (dateLen > 1) { //atleast one future purchase order exist
if (dateLogic.isBetween(false, DATE_ARRAY[dateIndex + 1], SO_ARRAY[soIndex].DATE_FIELD.value, "days", "[)")) //end date will be future purchase order
BUCKET[DATE_ARRAY[dateIndex]].push(SO_ARRAY[soIndex]);
} else if (dateLogic.isBetween(false, DATE_ARRAY[dateIndex], SO_ARRAY[soIndex].DATE_FIELD.value, "days", "[]")) //no future purchase order exist,end date will be start date or atonce date
BUCKET[DATE_ARRAY[dateIndex]].push(SO_ARRAY[soIndex]);
} else if (dateLen > 1 && dateIndex == (dateLen - 1)) { //if date is end date or last purchase order date and atleast one future purchase order exist
if (dateLogic.isBetween(DATE_ARRAY[dateIndex], false, SO_ARRAY[soIndex].DATE_FIELD.value, "days", "[)"))
BUCKET[DATE_ARRAY[dateIndex]].push(SO_ARRAY[soIndex]);
} else { //if date is inbetween purchase order date bucket
if (dateLogic.isBetween(DATE_ARRAY[dateIndex], DATE_ARRAY[dateIndex + 1], SO_ARRAY[soIndex].DATE_FIELD.value, "days", "[)"))
BUCKET[DATE_ARRAY[dateIndex]].push(SO_ARRAY[soIndex]);
}
}
}
return {
DATE_ARRAY: DATE_ARRAY,
BUCKET: BUCKET
};
},
consolidatePurchaseOrderByDate: function (dataObj, PO_ARRAY) {
//Consolidate Purchase Order by Bucket(date)
var CONSOLIDATED_PO_OBJECT = {};
for (var index = 0, len = PO_ARRAY.length; index < len; index++)
if (dateLogic.isBetween(dataObj._atoncedate, false, PO_ARRAY[index].DATE_FIELD.value, "days", "()")) { //Include only future date Purchase Order excluding today
if (!CONSOLIDATED_PO_OBJECT[PO_ARRAY[index].DATE_FIELD.value])
CONSOLIDATED_PO_OBJECT[PO_ARRAY[index].DATE_FIELD.value] = {
date: PO_ARRAY[index].DATE_FIELD.value,
quantityremaining: 0
};
CONSOLIDATED_PO_OBJECT[PO_ARRAY[index].DATE_FIELD.value].quantityremaining += Number(assignDefaultValue(PO_ARRAY[index].quantityremaining.value, 0));
}
return CONSOLIDATED_PO_OBJECT;
},
consolidateSalesOrderByBucket: function (dataObj, DATE_ARRAY, BUCKET) {
//Consolidate Sales Order by Bucket(date)
var CONSOLIDATED_SO_OBJECT = {};
for (var dateIndex = 0, dateLen = DATE_ARRAY.length; dateIndex < dateLen; dateIndex++) {
if (!CONSOLIDATED_SO_OBJECT[DATE_ARRAY[dateIndex]])
CONSOLIDATED_SO_OBJECT[DATE_ARRAY[dateIndex]] = {
date: DATE_ARRAY[dateIndex] + (dateIndex == 0 ? ' (AtOnce)' : ''),
quantityremaining: 0
};
for (var bucketIndex = 0, bucketLen = BUCKET[DATE_ARRAY[dateIndex]].length; bucketIndex < bucketLen; bucketIndex++) {
CONSOLIDATED_SO_OBJECT[DATE_ARRAY[dateIndex]].quantityremaining += Number(assignDefaultValue(BUCKET[DATE_ARRAY[dateIndex]][bucketIndex].quantityremaining.value, 0));
}
}
return CONSOLIDATED_SO_OBJECT;
},
calculateATS: function (dataObj, DATE_ARRAY, BUCKET, PO_ARRAY, isForm) {
//Calculate ATS qunatity and ATS Object
var CONSOLIDATED_PO_OBJECT = dataManipulate.consolidatePurchaseOrderByDate(dataObj, PO_ARRAY);
LOG.debug('CONSOLIDATED_PO_OBJECT', CONSOLIDATED_PO_OBJECT);
var CONSOLIDATED_SO_OBJECT = dataManipulate.consolidateSalesOrderByBucket(dataObj, DATE_ARRAY, BUCKET);
LOG.debug('CONSOLIDATED_SO_OBJECT', CONSOLIDATED_SO_OBJECT);
var ATS = [],
tempObj = {},
summaryObj = {},
brandScopeATSObj = {},
eachSummaryCount = 0;
var CARRY_FORWARD = -0;
for (var index = 0, len = DATE_ARRAY.length; index < len; index++) {
tempObj["Date"] = index == 0 ? "AtOnce" : DATE_ARRAY[index];
tempObj["Carry_Forward"] = 0;
tempObj["ATS"] = 0;
//Adding Previous Carry Forward to ATS
tempObj["ATS"] += CARRY_FORWARD;
if (index == 0) //if AtOnce or Current Date, ATS will be ( Quantity Available - Quantity BackOrdered )
tempObj["ATS"] += Number(assignDefaultValue(dataObj._qtyavailable, 0)) - Number(assignDefaultValue(dataObj._qtybackordered, 0));
if (index == 1) //if First Future Purchase Order Date, ATS will have Sum of Remaining Quantities in Past Dated Purchase Order
tempObj["ATS"] += Number(assignDefaultValue(dataObj._totalpastpurchaseorder, 0));
if (CONSOLIDATED_PO_OBJECT[DATE_ARRAY[index]]) //If there exists Purchase Order/s on this date, then the quantity remaining will be added to ATS
tempObj["ATS"] += Number(assignDefaultValue(CONSOLIDATED_PO_OBJECT[DATE_ARRAY[index]].quantityremaining, 0));
if (CONSOLIDATED_SO_OBJECT[DATE_ARRAY[index]] && len > 1) //If there exists Sales Order on this date, then Substract Sales Order Remaining Quantity only if there exists a Future Dated Purchase Order , ie OFFSET is not calculated if there isn't any Future Dated Purchase Order
tempObj["ATS"] -= Number(assignDefaultValue(CONSOLIDATED_SO_OBJECT[DATE_ARRAY[index]].quantityremaining, 0));
//If request is from Suitelet Form,then generate Summary to be shown in Suitelet Form
if (isForm) {
eachSummaryCount = 0;
summaryObj = {
'SO_QUANTITY': (CONSOLIDATED_SO_OBJECT[DATE_ARRAY[index]] && len > 1) ? -Number(assignDefaultValue(CONSOLIDATED_SO_OBJECT[DATE_ARRAY[index]].quantityremaining, 0)) : 0,
};
if (index > 0) {
summaryObj['PO_QUANTITY'] = (CONSOLIDATED_PO_OBJECT[DATE_ARRAY[index]]) ? Number(assignDefaultValue(CONSOLIDATED_PO_OBJECT[DATE_ARRAY[index]].quantityremaining, 0)) : 0;
summaryObj['CARRY_FORWARD (Previous Bucket)'] = CARRY_FORWARD;
}
if (index == 0) {
summaryObj["QUANTITY AVAILABLE"] = Number(assignDefaultValue(dataObj._qtyavailable, 0));
summaryObj["QUANTITY BACKORDERED"] = -Number(assignDefaultValue(dataObj._qtybackordered, 0));
}
if (index == 1)
summaryObj["UNRECEIVED PAST PURCHASE ORDER"] = Number(assignDefaultValue(dataObj._totalpastpurchaseorder, 0));
tempObj["Summary"] = '';
for (var key in summaryObj) {
eachSummaryCount += summaryObj[key];
tempObj["Summary"] += key.toString() + ' = ' + summaryObj[key].toString() + '<br/>';
}
if (eachSummaryCount >= 0)
tempObj["Summary"] += '<span style="font-weight:bold;" >Net Value = ' + '<span style="color:green;" >' + eachSummaryCount.toString() + '</span>' + '</span>';
else
tempObj["Summary"] += '<span style="font-weight:bold;" >Net Value = ' + '<span style="color:red;" >' + eachSummaryCount.toString() + '</span>' + '</span>';
summaryObj = undefined;
summaryObj = {};
}
//ATS Value will always be greater than or equal to Zero. If the calculated ATS Value is negative, we will assign that value to Carry Forward and make ATS value as zero
if (tempObj["ATS"] < 0) {
tempObj["Carry_Forward"] = tempObj["ATS"];
tempObj["ATS"] = 0;
CARRY_FORWARD = tempObj["Carry_Forward"];
} else {
tempObj["Carry_Forward"] = 0;
CARRY_FORWARD = 0;
}
tempObj["ATS"] = tempObj["ATS"].toString();
tempObj["Carry_Forward"] = tempObj["Carry_Forward"].toString();
//If request is from Suitelet Form,then generate ATS array else generate BrandScope Object
if (isForm)
ATS.push(tempObj);
else {
if (index > 0) //If not AtOnce or current Date
brandScopeATSObj[dateLogic.formatDate(tempObj["Date"], BRANDSCOPE_SETUP.date_format.required_format)] = tempObj["ATS"];
else
brandScopeATSObj["At Once"] = tempObj["ATS"];
}
tempObj = undefined;
tempObj = {};
}
if (isForm)
LOG.debug('ATS array for Suitelet', ATS);
else
LOG.debug('brandScopeATSObj for Scheduler', brandScopeATSObj);
//If request is from Suitelet Form,then return ATS array else return BrandScope Object
if (isForm)
return ATS;
else
return brandScopeATSObj;
}
};
applyTryCatch(dataManipulate, "dataManipulate");
//Main or Root Function of this Custom Module. This will be the function(Object) that will be exposed to user(public)
var main = {
moment: moment, //exposing third party library in custom module
dateLogic: dateLogic, //exposing private functions in custom module
dataSets: dataSets, //exposing private functions in custom module
dataManipulate: dataManipulate, //exposing private functions in custom module
initialise: function () {
//Trigger initialse BrandScope Environment
var brandScopeSetup = dataSets.brandScopeSetup();
if (brandScopeSetup.length)
return dataManipulate.initialiseBrandScopeSetup(brandScopeSetup[0]);
return false;
},
getBrandScopeSetup: function () {
//retun BrandScope Environment
return BRANDSCOPE_SETUP;
},
cleanSlate: function (dataObj) {
//rreinitialsie Object Properties (variables)
return {
_internalid: dataObj._internalid,
_itemfield: dataObj._itemfield,
_locationfield: dataObj._locationfield
};
},
generateFormValues: function (dataObj) {
//if request is from Suitelet, then retun entire dataset
return main.generateATSValue(dataObj, true);
},
generateSchedulerValues: function (dataObj) {
//if request is from Scheduled Script, then return only the neccessary data
return main.generateATSValue(dataObj);
},
generateATSValue: function (dataObj, isForm) {
//Tirggering all prerequisite functions and then proceed with ATS calculation
//Cleaning dataObj and resetting it with only the neccassary values
dataObj = main.cleanSlate(dataObj);
var purchaseOrderArray = [],
FIRST_PURCHASE_ORDER_DATE = false,
salesOrderArray = [],
mappedATS = [],
bucketPeriod = {
DATE_ARRAY: [],
BUCKET: {}
};
//Setting AtOnce Date
dataObj._atoncedate = dateLogic.formatDate(moment(), BRANDSCOPE_SETUP.date_format.ns_format);
//Fetching Item Details
dataObj = dataSets.item(dataObj._internalid, dataObj._locationfield, dataObj);
//Fetching Purchase Order Details
purchaseOrderArray = dataSets.purchaseOrder(dataObj._internalid, dataObj._locationfield);
FIRST_PURCHASE_ORDER_DATE = purchaseOrderArray.length > 0 ? dataManipulate.findFirstPurchaseOrderDate(dataObj, purchaseOrderArray) : false;
//Fetching Sales Order Details
salesOrderArray = dataSets.salesOrder(dataObj._internalid, dataObj._locationfield, FIRST_PURCHASE_ORDER_DATE);
//Calculating OffSet
dataObj._totaloffset = dataManipulate.findOffset(dataObj, salesOrderArray, purchaseOrderArray);
//Calculating Past Purchase Order
dataObj._totalpastpurchaseorder = dataManipulate.findUnreceivedPastPurchaseOrder(dataObj, purchaseOrderArray);
//Initialse dataObj with the inventory details
var initialiseFields = ["_totalqtyavailable", "_totalqtycommitted", "_totalqtybackordered", "_totalqtyonorder"].concat(["_totaloffset", "_totalpastpurchaseorder", "_qtyavailable", "_qtycommitted", "_qtybackordered"]);
for (var index = 0, len = initialiseFields.length; index < len; index++)
if (!checkForParameter(dataObj[initialiseFields[index]]))
dataObj[initialiseFields[index]] = "0";
//Reflecting real NetSuite Inventory Details by taking into account OffSet value
//Calculating Quantity Comitted by taking in OffSet
dataObj._qtycommitted = (Number(dataObj._totalqtycommitted) - Number(dataObj._totaloffset)).toString();
//Calculating Quantity Available and Quantity Backordered
if (Number(dataObj._totaloffset) + Number(dataObj._totalqtyavailable) - Number(dataObj._totalqtybackordered) >= Number(0)) {
dataObj._qtyavailable = (Number(dataObj._totaloffset) + Number(dataObj._totalqtyavailable) - Number(dataObj._totalqtybackordered)).toString();
dataObj._qtybackordered = "0";
} else {
dataObj._qtyavailable = "0";
dataObj._qtybackordered = Math.abs(Number(dataObj._totaloffset) + Number(dataObj._totalqtyavailable) - Number(dataObj._totalqtybackordered)).toString();
}
//Creating Bucket Period
bucketPeriod = dataManipulate.createBucket(dataObj, salesOrderArray, purchaseOrderArray);
//Creating ATS Object
mappedATS = dataManipulate.calculateATS(dataObj, bucketPeriod.DATE_ARRAY, bucketPeriod.BUCKET, purchaseOrderArray, isForm);
LOG.debug("dataObj", dataObj);
LOG.debug("bucketPeriod.DATE_ARRAY", bucketPeriod.DATE_ARRAY);
LOG.debug("bucketPeriod.BUCKET", bucketPeriod.BUCKET);
LOG.debug("BRANDSCOPE_SETUP", BRANDSCOPE_SETUP);
if (isForm) //If request is from Suitelet form return all the neccessary values for setting up the form else return only the mapped ATS Value
return {
dataObj: dataObj,
purchaseOrderArray: purchaseOrderArray,
salesOrderArray: salesOrderArray,
bucketPeriod: bucketPeriod,
mappedATS: mappedATS
};
return mappedATS;
}
};
applyTryCatch(main, "main");
return main;
});
if (!Array.prototype.push) {
Array.prototype.push = function (x) {
this[this.length] = x;
return true;
};
}
if (!Array.prototype.pop) {
Array.prototype.pop = function () {
var response = this[this.length - 1];
this.length--;
return response;
};
}
if (!Array.prototype.remove) {
Array.prototype.remove = function (from, to) {
var rest = this.slice(parseInt(to || from) + 1 || this.length);
this.length = from < 0 ? this.length + from : from;
return this.push.apply(this, rest);
};
}
if (typeof Object.assign != "function") {
Object.defineProperty(Object, "assign", {
value: function assign(target, varArgs) {
"use strict";
if (target == null) {
throw new TypeError("Cannot convert undefined or null to object");
}
var to = Object(target);
for (var index = 1; index < arguments.length; index++) {
var nextSource = arguments[index];
if (nextSource != null) {
for (var nextKey in nextSource) {
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
to[nextKey] = nextSource[nextKey];
}
}
}
}
return to;
},
writable: true,
configurable: true
});
}
if (!Array.prototype.reduce) {
Object.defineProperty(Array.prototype, "reduce", {
value: function (callback /*, initialValue*/ ) {
if (this === null) {
throw new TypeError(
"Array.prototype.reduce " + "called on null or undefined"
);
}
if (typeof callback !== "function") {
throw new TypeError(callback + " is not a function");
}
var o = Object(this);
var len = o.length >>> 0;
var k = 0;
var value;
if (arguments.length >= 2) {
value = arguments[1];
} else {
while (k < len && !(k in o)) {
k++;
}
if (k >= len) {
throw new TypeError(
"Reduce of empty array " + "with no initial value"
);
}
value = o[k++];
}
while (k < len) {
if (k in o) {
value = callback(value, o[k], k, o);
}
k++;
}
return value;
}
});
}
if (!Array.prototype.map) {
Array.prototype.map = function (callback /*, thisArg*/ ) {
var T, A, k;
if (this == null) {
throw new TypeError("this is null or not defined");
}
var O = Object(this);
var len = O.length >>> 0;
if (typeof callback !== "function") {
throw new TypeError(callback + " is not a function");
}
if (arguments.length > 1) {
T = arguments[1];
}
A = new Array(len);
k = 0;
while (k < len) {
var kValue, mappedValue;
if (k in O) {
kValue = O[k];
mappedValue = callback.call(T, kValue, k, O);
A[k] = mappedValue;
}
k++;
}
return A;
};
}
if (!Array.prototype.filter) {
Array.prototype.filter = function (func, thisArg) {
"use strict";
if (!((typeof func === "Function" || typeof func === "function") && this))
throw new TypeError();
var len = this.length >>> 0,
res = new Array(len), // preallocate array
t = this,
c = 0,
i = -1;
if (thisArg === undefined) {
while (++i !== len) {
if (i in this) {
if (func(t[i], i, t)) {
res[c++] = t[i];
}
}
}
} else {
while (++i !== len) {
if (i in this) {
if (func.call(thisArg, t[i], i, t)) {
res[c++] = t[i];
}
}
}
}
res.length = c; // shrink down array to proper size
return res;
};
}
if (!Array.prototype.mapUsingReduce) {
Array.prototype.mapUsingReduce = function (callback, thisArg) {
return this.reduce(function (mappedArray, currentValue, index, array) {
mappedArray[index] = callback.call(thisArg, currentValue, index, array);
return mappedArray;
}, []);
};
}
BPL-6 JJ SL BrandScope ATS Unit Test.js
/**
* @NApiVersion 2.x
* @NScriptType Suitelet
* @NModuleScope SameAccount
*/
/*******************************************************************************
* * Jobin & Jismi IT Services | BrandScope | ATS Unit Test*
* **************************************************************************
*
* Author: Jobin & Jismi IT Services LLP
*
* Date Created : 05-March-2019
*
* Created By : Manu Antony, Jobin & Jismi IT Services LLP
*
* SCRIPT ID: customscript_jj_bpl6_sl_ats_unit_test
*
* DEPLOYMENT ID: customdeploy_jj_bpl6_sl_ats_unit_test
*
* REVISION HISTORY
*
*
******************************************************************************/
"use strict";
const SCRIPT_OBJ = {
SCRIPT_ID: "customscript_jj_bpl6_sl_ats_unit_test",
DEPLOYMENT_ID: ["customdeploy_jj_bpl6_sl_ats_unit_test"]
};
var BRANDSCOPE_SETUP = {
brandscope_fields: {
atstoken: "",
atsendpoint: "",
nsemployee: "-5"
},
item_fields: {
sku: "internalid",
color: "internalid",
size: "internalid",
brand: "internalid"
},
salesorder_fields: {
date: "trandate"
},
purchaseorder_fields: {
date: "duedate"
},
date_format: {
ns_format: "",
required_format: "DD/MM/YYYY"
}
};
define(["N/record", "N/ui/serverWidget", "N/url", "N/log", "./BPL-7 JJ BrandScope Logic"],
function (record, serverWidget, url, log, brandscopelogic) {
//To check whether a value exists in parameter
function checkForParameter(parameter, parameterName) {
if (parameter !== null && parameter !== undefined && parameter !== false && parameter !== "null" && parameter !== "undefined" && parameter !== "false" && parameter != "" && parameter != " ")
return true;
if (parameterName)
log.debug("Empty Value found", "Empty Value for parameter " + parameterName);
return false;
}
//To assign a default value if the it is empty
function assignDefaultValue(value, defaultValue) {
if (checkForParameter(value)) return value;
return defaultValue;
}
//To reject predefined set of values
function rejectThisValues(value) {
var rejectObj = {
null: true,
undefined: true,
NaN: true,
0: true,
false: true,
"": true
};
return rejectObj[value] ? false : true;
}
//Common Try-Catch function
function applyTryCatch(DATA_OBJ, NAME) {
function tryCatch(myfunction, key) {
return function () {
try {
return myfunction.apply(this, arguments);
} catch (e) {
log.error("error in " + key, e);
return false;
}
};
}
for (var key in DATA_OBJ) {
if (typeof DATA_OBJ[key] === "function") {
DATA_OBJ[key] = tryCatch(DATA_OBJ[key], NAME + "." + key);
}
}
}
//Apply Styles
var formatText = {
allignContent: function (value, alignValue, noValue) {
alignValue = assignDefaultValue(alignValue, "center");
noValue = assignDefaultValue(noValue, "-");
return ('<p align="' + alignValue + '">' + assignDefaultValue(value, noValue) + "</p>");
},
applyLink: function (hrefURL, text) {
hrefURL = assignDefaultValue(hrefURL, "javascript:void(0);");
text = assignDefaultValue(text, '- NIL -');
return '<a href="' + hrefURL + '" target="_blank" >' + text + '</a>';
},
applyStyle: function (param) {
var str = '<span style="';
if (param.FONT_WEIGHT) str += "font-weight:" + param.FONT_WEIGHT + ";";
if (param.FONT_COLOR) str += "color:" + param.FONT_COLOR + ";";
if (param.FONT_SIZE) str += "font-size:" + param.FONT_SIZE + ";";
if (param.FONT_STYLE) str += "font-style:" + param.FONT_STYLE + ";";
if (param.FONT_FAMILY) str += "font-family:" + param.FONT_FAMILY + ";";
str += '"> ' + param.VALUE + " </span>";
return str;
}
};
applyTryCatch(formatText, "formatText");
//Main or Root Function of Suitelet -> Used to set form
var main = {
setformFieldProperty: function (field, propObj) {
var setFieldProp = {
updateLayoutType: function (field, value) {
field.updateLayoutType({
layoutType: value
});
},
updateBreakType: function (field, value) {
field.updateBreakType({
breakType: value
});
},
updateDisplayType: function (field, value) {
field.updateDisplayType({
displayType: value
});
},
setHelpText: function (field, value) {
field.setHelpText({
help: value
});
},
isMandatory: function (field, value) {
field.isMandatory = value;
},
defaultValue: function (field, value) {
field.defaultValue = value;
}
};
for (var prop in propObj)
if (propObj[prop]) setFieldProp[prop](field, propObj[prop]);
},
setProductToTraceContent: function (form, dataObj) {
var fieldgroup = form.addFieldGroup({
id: "_product_trace",
label: "Product to Trace"
});
// fieldgroup.isSingleColumn = true;
var _itemfield = form.addField({
id: "_itemfield",
type: serverWidget.FieldType.SELECT,
label: "ITEM",
source: "item",
container: "_product_trace"
});
main.setformFieldProperty(_itemfield, {
updateLayoutType: serverWidget.FieldLayoutType.NORMAL,
setHelpText: "Select an Item",
isMandatory: true,
defaultValue: dataObj._itemfield
});
var _locationfield = form.addField({
id: "_locationfield",
type: serverWidget.FieldType.SELECT,
label: "LOCATION",
source: "location",
container: "_product_trace"
});
main.setformFieldProperty(_locationfield, {
updateLayoutType: serverWidget.FieldLayoutType.NORMAL,
setHelpText: "Only the selected location will be considered for ATS calculation",
isMandatory: true,
defaultValue: dataObj._locationfield
});
},
setProductDetailContent: function (form, dataObj) {
form.addFieldGroup({
id: "_product_details",
label: "Product Details"
});
var _displayname = form.addField({
id: "_displayname",
type: serverWidget.FieldType.TEXT,
label: "DISPLAY NAME/CODE",
container: "_product_details"
});
main.setformFieldProperty(_displayname, {
updateLayoutType: serverWidget.FieldLayoutType.NORMAL,
updateBreakType: serverWidget.FieldBreakType.STARTCOL,
updateDisplayType: serverWidget.FieldDisplayType.DISABLED,
defaultValue: dataObj._displayname
});
var _itemid = form.addField({
id: "_itemid",
type: serverWidget.FieldType.TEXT,
label: "ITEM NAME/NUMBER",
container: "_product_details"
});
main.setformFieldProperty(_itemid, {
updateLayoutType: serverWidget.FieldLayoutType.NORMAL,
updateBreakType: serverWidget.FieldBreakType.STARTCOL,
updateDisplayType: serverWidget.FieldDisplayType.DISABLED,
defaultValue: dataObj._itemid
});
var _sku = form.addField({
id: "_sku",
type: serverWidget.FieldType.TEXT,
label: "SKU",
container: "_product_details"
});
main.setformFieldProperty(_sku, {
setHelpText: formatText.applyStyle({
FONT_WEIGHT: "normal",
VALUE: "Value is sourced from the ",
FONT_COLOR: "black"
}) + formatText.applyStyle({
FONT_WEIGHT: "bold",
VALUE: BRANDSCOPE_SETUP.item_fields.sku,
FONT_COLOR: "green"
}) + formatText.applyStyle({
FONT_WEIGHT: "normal",
VALUE: " field on Item Record",
FONT_COLOR: "black"
}),
updateLayoutType: serverWidget.FieldLayoutType.NORMAL,
updateBreakType: serverWidget.FieldBreakType.STARTCOL,
updateDisplayType: serverWidget.FieldDisplayType.DISABLED,
defaultValue: dataObj._sku
});
var _color = form.addField({
id: "_color",
type: serverWidget.FieldType.TEXT,
label: "COLOR",
container: "_product_details"
});
main.setformFieldProperty(_color, {
setHelpText: formatText.applyStyle({
FONT_WEIGHT: "normal",
VALUE: "Value is sourced from the ",
FONT_COLOR: "black"
}) + formatText.applyStyle({
FONT_WEIGHT: "bold",
VALUE: BRANDSCOPE_SETUP.item_fields.color,
FONT_COLOR: "green"
}) + formatText.applyStyle({
FONT_WEIGHT: "normal",
VALUE: " field on Item Record",
FONT_COLOR: "black"
}),
updateLayoutType: serverWidget.FieldLayoutType.NORMAL,
updateBreakType: serverWidget.FieldBreakType.STARTCOL,
updateDisplayType: serverWidget.FieldDisplayType.DISABLED,
defaultValue: dataObj._color
});
var _size = form.addField({
id: "_size",
type: serverWidget.FieldType.TEXT,
label: "SIZE",
container: "_product_details"
});
main.setformFieldProperty(_size, {
setHelpText: formatText.applyStyle({
FONT_WEIGHT: "normal",
VALUE: "Value is sourced from the ",
FONT_COLOR: "black"
}) + formatText.applyStyle({
FONT_WEIGHT: "bold",
VALUE: BRANDSCOPE_SETUP.item_fields.size,
FONT_COLOR: "green"
}) + formatText.applyStyle({
FONT_WEIGHT: "normal",
VALUE: " field on Item Record",
FONT_COLOR: "black"
}),
updateLayoutType: serverWidget.FieldLayoutType.NORMAL,
updateBreakType: serverWidget.FieldBreakType.STARTCOL,
updateDisplayType: serverWidget.FieldDisplayType.DISABLED,
defaultValue: dataObj._size
});
var _brand = form.addField({
id: "_brand",
type: serverWidget.FieldType.TEXT,
label: "Brand",
container: "_product_details"
});
main.setformFieldProperty(_brand, {
setHelpText: formatText.applyStyle({
FONT_WEIGHT: "normal",
VALUE: "Value is sourced from the ",
FONT_COLOR: "black"
}) + formatText.applyStyle({
FONT_WEIGHT: "bold",
VALUE: BRANDSCOPE_SETUP.item_fields.brand,
FONT_COLOR: "green"
}) + formatText.applyStyle({
FONT_WEIGHT: "normal",
VALUE: " field on Item Record",
FONT_COLOR: "black"
}),
updateLayoutType: serverWidget.FieldLayoutType.NORMAL,
updateBreakType: serverWidget.FieldBreakType.STARTCOL,
updateDisplayType: serverWidget.FieldDisplayType.DISABLED,
defaultValue: dataObj._brand
});
},
setInventoryDetailContent: function (form, dataObj) {
form.addFieldGroup({
id: "_inventory_details",
label: "NetSuite Inventory Details"
});
var _atoncedate = form.addField({
id: "_atoncedate",
type: serverWidget.FieldType.DATE,
label: "NetSuite AtOnce Date",
container: "_inventory_details"
});
main.setformFieldProperty(_atoncedate, {
setHelpText: "Current Date",
updateLayoutType: serverWidget.FieldLayoutType.NORMAL,
updateBreakType: serverWidget.FieldBreakType.STARTCOL,
updateDisplayType: serverWidget.FieldDisplayType.DISABLED,
defaultValue: dataObj._atoncedate
});
var _totalqtyavailable = form.addField({
id: "_totalqtyavailable",
type: serverWidget.FieldType.INTEGER,
label: "NetSuite QUANTITY AVAILABLE",
container: "_inventory_details"
});
main.setformFieldProperty(_totalqtyavailable, {
updateLayoutType: serverWidget.FieldLayoutType.NORMAL,
updateBreakType: serverWidget.FieldBreakType.STARTCOL,
updateDisplayType: serverWidget.FieldDisplayType.DISABLED,
defaultValue: dataObj._totalqtyavailable
});
var _totalqtycommitted = form.addField({
id: "_totalqtycommitted",
type: serverWidget.FieldType.INTEGER,
label: "NetSuite QUANTITY COMMITTED",
container: "_inventory_details"
});
main.setformFieldProperty(_totalqtycommitted, {
updateLayoutType: serverWidget.FieldLayoutType.NORMAL,
updateBreakType: serverWidget.FieldBreakType.STARTCOL,
updateDisplayType: serverWidget.FieldDisplayType.DISABLED,
defaultValue: dataObj._totalqtycommitted
});
var _totalqtybackordered = form.addField({
id: "_totalqtybackordered",
type: serverWidget.FieldType.INTEGER,
label: "NetSuite QUANTITY BACKORDERED",
container: "_inventory_details"
});
main.setformFieldProperty(_totalqtybackordered, {
updateLayoutType: serverWidget.FieldLayoutType.NORMAL,
updateBreakType: serverWidget.FieldBreakType.STARTCOL,
updateDisplayType: serverWidget.FieldDisplayType.DISABLED,
defaultValue: dataObj._totalqtybackordered
});
var _totalqtyonorder = form.addField({
id: "_totalqtyonorder",
type: serverWidget.FieldType.INTEGER,
label: "NetSuite QUANTITY ONORDER",
container: "_inventory_details"
});
main.setformFieldProperty(_totalqtyonorder, {
updateLayoutType: serverWidget.FieldLayoutType.NORMAL,
updateBreakType: serverWidget.FieldBreakType.STARTCOL,
updateDisplayType: serverWidget.FieldDisplayType.DISABLED,
defaultValue: dataObj._totalqtyonorder
});
},
setCalculatedInventoryDetailContent: function (form, dataObj) {
form.addFieldGroup({
id: "_calculated_inventory_details",
label: "Calculated Inventory Details"
});
var _totaloffset = form.addField({
id: "_totaloffset",
type: serverWidget.FieldType.INTEGER,
label: "OFFSET QUANTITY",
container: "_calculated_inventory_details"
});
main.setformFieldProperty(_totaloffset, {
updateLayoutType: serverWidget.FieldLayoutType.NORMAL,
setHelpText: formatText.applyStyle({
FONT_WEIGHT: "bold",
VALUE: "All Committed Quantity of Sales Order on or after First Future Dated Purchase Order",
FONT_COLOR: "black"
}),
updateBreakType: serverWidget.FieldBreakType.NONE,
updateDisplayType: serverWidget.FieldDisplayType.DISABLED,
defaultValue: dataObj._totaloffset
});
var _qtyavailable = form.addField({
id: "_qtyavailable",
type: serverWidget.FieldType.INTEGER,
label: "QUANTITY AVAILABLE",
container: "_calculated_inventory_details"
});
main.setformFieldProperty(_qtyavailable, {
updateLayoutType: serverWidget.FieldLayoutType.NORMAL,
setHelpText: formatText.applyStyle({
FONT_WEIGHT: "bold",
VALUE: "NETSUITE QUANTITY AVAILABLE - NETSUITE QUANTITY BACKORDERED + OFFSET QUANTITY",
FONT_COLOR: "green"
}) + formatText.applyStyle({
FONT_WEIGHT: "bold",
VALUE: ", only if this value is positive else 0",
FONT_COLOR: "black"
}),
updateBreakType: serverWidget.FieldBreakType.NONE,
updateDisplayType: serverWidget.FieldDisplayType.DISABLED,
defaultValue: dataObj._qtyavailable
});
var _qtycommitted = form.addField({
id: "_qtycommitted",
type: serverWidget.FieldType.INTEGER,
label: "QUANTITY COMMITTED",
container: "_calculated_inventory_details"
});
main.setformFieldProperty(_qtycommitted, {
updateLayoutType: serverWidget.FieldLayoutType.NORMAL,
setHelpText: formatText.applyStyle({
FONT_WEIGHT: "bold",
VALUE: "NETSUITE QUANTITY COMMITTED - OFFSET QUANTITY",
FONT_COLOR: "green"
}) + formatText.applyStyle({
FONT_WEIGHT: "bold",
VALUE: ", only if this value is positive else 0",
FONT_COLOR: "black"
}),
updateBreakType: serverWidget.FieldBreakType.NONE,
updateDisplayType: serverWidget.FieldDisplayType.DISABLED,
defaultValue: dataObj._qtycommitted
});
var _qtybackordered = form.addField({
id: "_qtybackordered",
type: serverWidget.FieldType.INTEGER,
label: "QUANTITY BACKORDERED",
container: "_calculated_inventory_details"
});
main.setformFieldProperty(_qtybackordered, {
updateLayoutType: serverWidget.FieldLayoutType.NORMAL,
setHelpText: formatText.applyStyle({
FONT_WEIGHT: "bold",
VALUE: "NETSUITE QUANTITY AVAILABLE - NETSUITE QUANTITY BACKORDERED + OFFSET QUANTITY",
FONT_COLOR: "green"
}) + formatText.applyStyle({
FONT_WEIGHT: "bold",
VALUE: ", only if this value is negative else 0",
FONT_COLOR: "black"
}),
updateBreakType: serverWidget.FieldBreakType.NONE,
updateDisplayType: serverWidget.FieldDisplayType.DISABLED,
defaultValue: dataObj._qtybackordered
});
var _totalpastpurchaseorder = form.addField({
id: "_totalpastpurchaseorder",
type: serverWidget.FieldType.INTEGER,
label: "Unreceived Past Purchase Order",
container: "_calculated_inventory_details"
});
main.setformFieldProperty(_totalpastpurchaseorder, {
updateLayoutType: serverWidget.FieldLayoutType.NORMAL,
setHelpText: formatText.applyStyle({
FONT_WEIGHT: "bold",
VALUE: "Total Unreceived Quantity of Past Dated Purchase Order (including today). This value will be added to First Future Dated Purchase Order",
FONT_COLOR: "black"
}),
updateBreakType: serverWidget.FieldBreakType.NONE,
updateDisplayType: serverWidget.FieldDisplayType.DISABLED,
defaultValue: dataObj._totalpastpurchaseorder
});
},
setBodyFields: function (form, dataObj) {
//Product to Trace
main.setProductToTraceContent(form, dataObj);
//Product Details
main.setProductDetailContent(form, dataObj);
//Inventory Details
main.setInventoryDetailContent(form, dataObj);
//Calculated Inventory Details
main.setCalculatedInventoryDetailContent(form, dataObj);
},
setSavedSearchResultAsSublist: function (SUBLIST_FIELD, dataObj, DATA_ARRAY, RECORD_TYPE) {
//To initilize sublist columns
var coulmnDetails = {};
if (DATA_ARRAY.length > 0)
for (var key in DATA_ARRAY[0]) {
coulmnDetails[key] = ("custpage_" + key).toLowerCase();
SUBLIST_FIELD.addField({
id: coulmnDetails[key],
label: key.toString().trim().toUpperCase(),
type: serverWidget.FieldType.TEXT
});
}
coulmnDetails.summary = "custpage_summary".toLowerCase();
SUBLIST_FIELD.addField({
id: coulmnDetails.summary,
label: "summary".toString().trim().toUpperCase(),
type: serverWidget.FieldType.TEXT
});
for (var index = 0, len = DATA_ARRAY.length; index < len; index++) {
for (var key in coulmnDetails) {
if (key == "summary")
SUBLIST_FIELD.setSublistValue({
id: coulmnDetails[key],
line: index,
value: formatText.applyStyle(Object.assign({
FONT_WEIGHT: "bold"
}, brandscopelogic.dateLogic.isBetween(dataObj._atoncedate, false, DATA_ARRAY[index].DATE_FIELD.value, "days", "()") ? {
VALUE: "Future Dated",
FONT_COLOR: "green"
} : {
VALUE: "Past Dated",
FONT_COLOR: "red"
}))
});
else if (key == "internalid")
//Set link to netsuite record if key is internalid
SUBLIST_FIELD.setSublistValue({
id: coulmnDetails[key],
line: index,
value: formatText.applyLink(url.resolveRecord({
recordType: RECORD_TYPE,
recordId: DATA_ARRAY[index][key].value,
isEditMode: false
}), DATA_ARRAY[index][key].value)
});
else if (DATA_ARRAY[index][key].text || DATA_ARRAY[index][key].value)
//Set value to sublist field only if there exists a value
SUBLIST_FIELD.setSublistValue({
id: coulmnDetails[key],
line: index,
value: DATA_ARRAY[index][key].text ?
DATA_ARRAY[index][key].text : DATA_ARRAY[index][key].value
});
}
}
},
setObjectAsSublist: function (SUBLIST_FIELD, DATA_ARRAY) {
//To initilize sublist columns
var coulmnDetails = {};
if (DATA_ARRAY.length > 0)
for (var key in DATA_ARRAY[0]) {
coulmnDetails[key] = ("custpage_" + key).toLowerCase();
SUBLIST_FIELD.addField({
id: coulmnDetails[key],
label: key.toString().trim().toUpperCase(),
type: serverWidget.FieldType.TEXT
});
}
for (var index = 0, len = DATA_ARRAY.length; index < len; index++) {
for (var key in coulmnDetails) {
if (DATA_ARRAY[index][key])
//Set value to sublist field only if there exists a value
SUBLIST_FIELD.setSublistValue({
id: coulmnDetails[key],
line: index,
value: DATA_ARRAY[index][key]
});
}
}
},
setTabField: function (form, dataObj, SO_ARRAY, PO_ARRAY, bucketPeriod, mappedATS) {
//Adding Subtab for showing Summary Detils
form.addTab({
id: "_summary_details_tab",
label: "Summary"
});
//Sales Order Tab Details
form.addSubtab({
id: '_summary_salesorder',
tab: '_summary_details_tab',
label: 'Sales Order'
});
var _sodatefield = form.addField({
id: "_sodatefield",
type: serverWidget.FieldType.TEXT,
label: "DATE_FIELD",
container: "_summary_salesorder"
});
main.setformFieldProperty(_sodatefield, {
updateLayoutType: serverWidget.FieldLayoutType.NORMAL,
setHelpText: "The Date Field in Sales Order taken for consideration. Its sourced from BrandScope Setup. If none is given," + formatText.applyStyle({
FONT_WEIGHT: "bold",
VALUE: "trandate",
FONT_COLOR: "green"
}) + " is used as default ",
updateBreakType: serverWidget.FieldBreakType.NONE,
updateDisplayType: serverWidget.FieldDisplayType.DISABLED,
defaultValue: assignDefaultValue(BRANDSCOPE_SETUP.salesorder_fields.date, "trandate")
});
main.setSavedSearchResultAsSublist(form.addSublist({
id: "_salesorder_sublist",
type: serverWidget.SublistType.LIST,
tab: "_summary_salesorder",
label: "Sales Order"
}), dataObj, SO_ARRAY, record.Type.SALES_ORDER);
//Purchase Order Tab Details
form.addSubtab({
id: '_summary_purchaseorder',
tab: '_summary_details_tab',
label: 'Purchase Order'
});
var _podatefield = form.addField({
id: "_podatefield",
type: serverWidget.FieldType.TEXT,
label: "DATE_FIELD",
container: "_summary_purchaseorder"
});
main.setformFieldProperty(_podatefield, {
updateLayoutType: serverWidget.FieldLayoutType.NORMAL,
setHelpText: "The Date Field in Purchase Order taken for consideration. Its sourced from BrandScope Setup. If none is given," + formatText.applyStyle({
FONT_WEIGHT: "bold",
VALUE: "duedate",
FONT_COLOR: "green"
}) + " is used as default",
updateBreakType: serverWidget.FieldBreakType.NONE,
updateDisplayType: serverWidget.FieldDisplayType.DISABLED,
defaultValue: assignDefaultValue(BRANDSCOPE_SETUP.purchaseorder_fields.date, "duedate")
});
main.setSavedSearchResultAsSublist(form.addSublist({
id: "_purchaseorder_sublist",
type: serverWidget.SublistType.LIST,
tab: "_summary_purchaseorder",
label: "Purchase Order"
}), dataObj, PO_ARRAY, record.Type.PURCHASE_ORDER);
//Consolidated Sales Order Details
var consolidateSO = brandscopelogic.dataManipulate.consolidateSalesOrderByBucket(dataObj, bucketPeriod.DATE_ARRAY, bucketPeriod.BUCKET);
main.setObjectAsSublist(form.addSublist({
id: "_consolidated_so_sublist",
type: serverWidget.SublistType.LIST,
tab: "_summary_details_tab",
label: "Consolidated Sales Order"
}), Object.keys(consolidateSO).map(function (e) {
return {
date: consolidateSO[e].date.toString(),
quantityremaining: consolidateSO[e].quantityremaining.toString()
};
}));
//Consolidated Purchase Order Details
var consolidatePO = brandscopelogic.dataManipulate.consolidatePurchaseOrderByDate(dataObj, PO_ARRAY);
// reducer function helper take obj and return the actual reducer function
var reducerFunctionHelper = function (obj) {
return function (accumulator, currentKey) {
accumulator.push({
date: obj[currentKey].date.toString(),
quantityremaining: obj[currentKey].quantityremaining.toString()
});
return accumulator;
};
};
main.setObjectAsSublist(form.addSublist({
id: "_consolidated_po_sublist",
type: serverWidget.SublistType.LIST,
tab: "_summary_details_tab",
label: "Consolidated Purchase Order"
}), // Object.keys(consolidatePO).reduce(reducerFunctionHelper(consolidatePO), [])
Object.keys(consolidatePO).map(function (e) {
return {
date: consolidatePO[e].date.toString(),
quantityremaining: consolidatePO[e].quantityremaining.toString()
};
}));
//ATS Tab Details
main.setObjectAsSublist(form.addSublist({
id: "_ats_sublist",
type: serverWidget.SublistType.LIST,
tab: "_summary_details_tab",
label: "ATS Details"
}), mappedATS);
},
generateForm: function (dataObj) {
log.debug("generateForm", "generateForm");
//initialize form
var form = serverWidget.createForm({
title: "Brandscope ATS Calculation"
});
form.addSubmitButton({
id: "_search_button",
label: "Search"
});
var _hiddeninlinehtml = form.addField({
id: '_hidden_inline_html_field',
type: serverWidget.FieldType.INLINEHTML,
label: 'Inline HTML'
});
dataObj._hiddeninlinehtml = '';
var purchaseOrderArray = [],
salesOrderArray = [],
mappedATS = [],
bucketPeriod = {
DATE_ARRAY: [],
BUCKET: {}
};
if (brandscopelogic.initialise()) {
BRANDSCOPE_SETUP = brandscopelogic.getBrandScopeSetup();
dataObj._atoncedate = brandscopelogic.dateLogic.formatDate(brandscopelogic.moment(), BRANDSCOPE_SETUP.date_format.ns_format);
if (checkForParameter(dataObj._itemfield) && checkForParameter(dataObj._locationfield)) {
dataObj = brandscopelogic.dataSets.item(dataObj._itemfield, dataObj._locationfield, dataObj);
if (dataObj._internalid) {
var generateFormValues = brandscopelogic.generateFormValues(dataObj, true);
dataObj = generateFormValues.dataObj;
purchaseOrderArray = generateFormValues.purchaseOrderArray;
salesOrderArray = generateFormValues.salesOrderArray;
bucketPeriod = generateFormValues.bucketPeriod;
mappedATS = generateFormValues.mappedATS;
} else
dataObj._hiddeninlinehtml = '<script>alert("No Inventory Details found for this product at this location")</script>';
}
} else
dataObj._hiddeninlinehtml = '<script>alert("Please Setup BrandScope Settings")</script>';
//To set data on Body field of form
main.setBodyFields(form, dataObj);
//To set data on Tab section of form
main.setTabField(form, dataObj, salesOrderArray, purchaseOrderArray, bucketPeriod, mappedATS);
//Set value to Inline HTML field if any
main.setformFieldProperty(_hiddeninlinehtml, {
updateDisplayType: serverWidget.FieldDisplayType.DISABLED,
defaultValue: assignDefaultValue(dataObj._hiddeninlinehtml, '')
});
return form;
},
onRequest: function (context) {
if (context.request.method === "GET" || context.request.method === "POST") {
var form = main.generateForm({
_itemfield: assignDefaultValue(context.request.parameters._itemfield, ""),
_locationfield: assignDefaultValue(context.request.parameters._locationfield, "")
});
if (form) {
context.response.writePage(form);
return true;
}
}
context.response.write(main.errorInProcess());
return false;
},
errorInProcess: function () {
return '<html><head><script>(function () {alert("Something went wrong");window.close();}());</script></head><body></body></html>';
}
};
applyTryCatch(main, "main");
return main;
});
if (!Array.prototype.push) {
Array.prototype.push = function (x) {
this[this.length] = x;
return true;
};
}
if (!Array.prototype.pop) {
Array.prototype.pop = function () {
var response = this[this.length - 1];
this.length--;
return response;
};
}
if (!Array.prototype.remove) {
Array.prototype.remove = function (from, to) {
var rest = this.slice(parseInt(to || from) + 1 || this.length);
this.length = from < 0 ? this.length + from : from;
return this.push.apply(this, rest);
};
}
if (typeof Object.assign != "function") {
Object.defineProperty(Object, "assign", {
value: function assign(target, varArgs) {
"use strict";
if (target == null) {
throw new TypeError("Cannot convert undefined or null to object");
}
var to = Object(target);
for (var index = 1; index < arguments.length; index++) {
var nextSource = arguments[index];
if (nextSource != null) {
for (var nextKey in nextSource) {
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
to[nextKey] = nextSource[nextKey];
}
}
}
}
return to;
},
writable: true,
configurable: true
});
}
if (!Array.prototype.reduce) {
Object.defineProperty(Array.prototype, "reduce", {
value: function (callback /*, initialValue*/ ) {
if (this === null) {
throw new TypeError(
"Array.prototype.reduce " + "called on null or undefined"
);
}
if (typeof callback !== "function") {
throw new TypeError(callback + " is not a function");
}
var o = Object(this);
var len = o.length >>> 0;
var k = 0;
var value;
if (arguments.length >= 2) {
value = arguments[1];
} else {
while (k < len && !(k in o)) {
k++;
}
if (k >= len) {
throw new TypeError(
"Reduce of empty array " + "with no initial value"
);
}
value = o[k++];
}
while (k < len) {
if (k in o) {
value = callback(value, o[k], k, o);
}
k++;
}
return value;
}
});
}
if (!Array.prototype.map) {
Array.prototype.map = function (callback /*, thisArg*/ ) {
var T, A, k;
if (this == null) {
throw new TypeError("this is null or not defined");
}
var O = Object(this);
var len = O.length >>> 0;
if (typeof callback !== "function") {
throw new TypeError(callback + " is not a function");
}
if (arguments.length > 1) {
T = arguments[1];
}
A = new Array(len);
k = 0;
while (k < len) {
var kValue, mappedValue;
if (k in O) {
kValue = O[k];
mappedValue = callback.call(T, kValue, k, O);
A[k] = mappedValue;
}
k++;
}
return A;
};
}
if (!Array.prototype.filter) {
Array.prototype.filter = function (func, thisArg) {
"use strict";
if (!((typeof func === "Function" || typeof func === "function") && this))
throw new TypeError();
var len = this.length >>> 0,
res = new Array(len), // preallocate array
t = this,
c = 0,
i = -1;
if (thisArg === undefined) {
while (++i !== len) {
if (i in this) {
if (func(t[i], i, t)) {
res[c++] = t[i];
}
}
}
} else {
while (++i !== len) {
if (i in this) {
if (func.call(thisArg, t[i], i, t)) {
res[c++] = t[i];
}
}
}
}
res.length = c; // shrink down array to proper size
return res;
};
}
if (!Array.prototype.mapUsingReduce) {
Array.prototype.mapUsingReduce = function (callback, thisArg) {
return this.reduce(function (mappedArray, currentValue, index, array) {
mappedArray[index] = callback.call(thisArg, currentValue, index, array);
return mappedArray;
}, []);
};
}
BPL-7 JJ SS BrandScope Scheduler.js
/**
* @NApiVersion 2.x
* @NScriptType ScheduledScript
* @NModuleScope SameAccount
*/
/*******************************************************************************
* * Jobin & Jismi IT Services | BrandScope | Scheduler*
* **************************************************************************
*
* Author: Jobin & Jismi IT Services LLP
*
* Date Created : 25-March-2019
*
* Created By : Manu Antony, Jobin & Jismi IT Services LLP
*
* SCRIPT ID: customscript_jj_bpl7_ss_scheduler
*
* DEPLOYMENT ID: customdeploy_jj_bpl7_ss_scheduler_0, customdeploy_jj_bpl7_ss_scheduler_1
*
* REVISION HISTORY
*
*
******************************************************************************/
"use strict";
const SCRIPT_OBJ = {
SCRIPT_ID: "customscript_jj_bpl7_ss_scheduler",
DEPLOYMENT_ID: ["customdeploy_jj_bpl7_ss_scheduler_0", "customdeploy_jj_bpl7_ss_scheduler_1"]
};
var BRANDSCOPE_SETUP = {
brandscope_fields: {
atstoken: "",
atsendpoint: "",
nsemployee: "-5"
},
item_fields: {
sku: "internalid",
color: "internalid",
size: "internalid",
brand: "internalid"
},
salesorder_fields: {
date: "trandate"
},
purchaseorder_fields: {
date: "duedate"
},
date_format: {
ns_format: "",
required_format: "DD/MM/YYYY"
}
};
define(["N/runtime", "N/task", "N/util", "N/http", "N/https", "N/record", 'N/config', "N/log", "./BPL-7 JJ BrandScope Logic"],
function (runtime, task, util, http, https, record, config, log, brandscopelogic) {
//To check whether a value exists in parameter
function checkForParameter(parameter, parameterName) {
if (parameter !== null && parameter !== undefined && parameter !== false && parameter !== "null" && parameter !== "undefined" && parameter !== "false" && parameter !== "" && parameter !== " ")
return true;
if (parameterName)
log.debug("Empty Value found", "Empty Value for parameter " + parameterName);
return false;
}
//To assign a default value if the it is empty
function assignDefaultValue(value, defaultValue) {
if (checkForParameter(value)) return value;
return defaultValue;
}
//To reject predefined set of values
function rejectThisValues(value) {
var rejectObj = {
null: true,
undefined: true,
NaN: true,
0: true,
false: true,
"": true
};
return rejectObj[value] ? false : true;
}
//Common Try-Catch function
function applyTryCatch(DATA_OBJ, NAME) {
function tryCatch(myfunction, key) {
return function () {
try {
return myfunction.apply(this, arguments);
} catch (e) {
log.error("error in " + key, e);
return false;
}
};
}
for (var key in DATA_OBJ) {
if (typeof DATA_OBJ[key] === "function") {
DATA_OBJ[key] = tryCatch(DATA_OBJ[key], NAME + "." + key);
}
}
}
//Encapsulates Scheduler Functions
var scheduler = {
currentScript: undefined,
run: function (key) {
return ({
initialise: function () {
return scheduler.initialise();
},
id: function () {
return scheduler.getScriptId();
},
deploymentId: function () {
return scheduler.getDeploymentId();
},
remainingUsage: function () {
return scheduler.getRemainingUsage();
},
parameter: function (args) {
return scheduler.getParameter(args);
},
reschedule: function (args) {
return scheduler.submitTask(args);
}
})[key](arguments);
},
initialise: function () {
this.currentScript = runtime.getCurrentScript();
},
getScriptId: function () {
return this.currentScript.id;
},
getDeploymentId: function () {
return this.currentScript.deploymentId;
},
getRemainingUsage: function () {
return Number(runtime.getCurrentScript().getRemainingUsage());
},
getParameter: function (args) {
//arguments = Array.prototype.slice(Array.prototype.shift.apply(arguments));
if (args[1])
return assignDefaultValue(this.currentScript.getParameter({
name: args[1].toString().toLowerCase().trim()
}), false);
return false;
},
submitTask: function (args) {
//if args, thens args are scriptId(String),deploymentId(String),params(Object)
return task.create({
taskType: task.TaskType.SCHEDULED_SCRIPT,
scriptId: assignDefaultValue(args[1] ? args[1] : this.run("id"), this.run("id")),
deploymentId: assignDefaultValue(args[2] ? args[2] : this.run("deploymentId"), this.run("deploymentId")),
params: (function () {
var tempObj = {};
if (args[3])
for (var key in args[3])
tempObj[key] = args[3][key];
return tempObj;
})()
}).submit();
}
};
applyTryCatch(scheduler, "scheduler");
//Manipulating DataSet such as formating,cleansing,calculting etc ...
var dataManipulate = {
cleanSlateProtocol: function (recordType, recordId) {
//Delete 'BrandScope Scheduler DataSet' entries
recordType = 'customrecord_jj_bpl7_brandscope_schedule';
if (!recordType && !recordId)
return false;
if (recordType && recordId)
return record.delete({
type: recordType,
id: recordId,
});
return false;
},
cleanseBranScopeSchedulerDataSet: function () {
//Clearing 'BrandScope Scheduler DataSet' on primary execution of Scheduled Script
var clensingArray = brandscopelogic.dataSets.brandScopeSchedulerDataSet();
clensingArray.forEach(function (eachResult) {
dataManipulate.cleanSlateProtocol('customrecord_jj_bpl7_brandscope_schedule', eachResult["Internal ID"].value);
});
return true;
},
processBrandScopeSchedulerDataSet: function () {
//Read data from 'BrandScope Scheduler DataSet'
function parseObject(obj) {
try {
return JSON.parse(obj);
} catch (er) {
return {};
}
}
return brandscopelogic.dataSets.brandScopeSchedulerDataSet().reduce(function (accumulator, currentValue) {
accumulator[currentValue.Item.value] = parseObject(
record.load({
type: 'customrecord_jj_bpl7_brandscope_schedule',
id: currentValue["Internal ID"].value,
isDynamic: false
})
.getValue({
fieldId: 'custrecord_bpl7_scheduler_data'
})
);
return accumulator;
}, {}); // Final result will be Object whose structure is { ITEM_ID : ATS VALUES }
},
processBrandScopeLocations: function (LOCATION_LIST) {
//Creates BrandScope Location Object
return brandscopelogic.dataSets.brandScopeLocation().reduce(function (accumulator, currentValue) {
LOCATION_LIST[currentValue.Location.value] = currentValue.Location.text;
accumulator[currentValue.Location.value] = currentValue.Abbreviations.value;
return accumulator;
}, {}); // Final result will be Object whose structure is { LOCATION_INTERNAL_ID : LOCATION_ABBREVIATIONS }
},
processBrandScopeBrands: function (BRAND_LIST) {
//Creates BrandScope Brand Object
return brandscopelogic.dataSets.brandScopeBrand().reduce(function (accumulator, currentValue) {
BRAND_LIST[currentValue.Brand.value] = currentValue.Brand.text;
if (accumulator[currentValue.Location.value])
accumulator[currentValue.Location.value].push(currentValue.Brand.value);
else
accumulator[currentValue.Location.value] = [currentValue.Brand.value];
return accumulator;
}, {}); // Final result will be Object whose structure is { LOCATION_INTERNAL_ID : [ BRAND_INTERNAL_ID ] }
},
processApprovedItems: function (LOCATION_LIST, LOCATION_MAP, BRAND_LIST) {
//Creates ATS Object for only the aproved items,brands and location
return brandscopelogic.dataSets.approvedItems(Object.keys(LOCATION_LIST), Object.keys(BRAND_LIST))
.reduce(function (accumulator, currentValue) {
if (accumulator[currentValue['Internal ID'].value])
accumulator[currentValue['Internal ID'].value].push(Object.assign(
currentValue, {
WarehouseCode: {
value: assignDefaultValue(LOCATION_MAP[currentValue['Inventory Location'].value], ''),
text: null
}
}));
else
accumulator[currentValue['Internal ID'].value] = [Object.assign(
currentValue, {
WarehouseCode: {
value: assignDefaultValue(LOCATION_MAP[currentValue['Inventory Location'].value], ''),
text: null
}
})];
return accumulator;
}, {});
},
setBrandScopeSchedulerDataSetInstance: function (item, itemArray, BRAND_MAP) {
//Create 'BrandScope Scheduler DataSet' instance
var brandScopeSchedulerDataSetRecord = record.create({
type: 'customrecord_jj_bpl7_brandscope_schedule',
isDynamic: false
});
brandScopeSchedulerDataSetRecord.setValue({
fieldId: 'custrecord_bpl7_scheduler_item', //ITEM
value: item,
ignoreFieldChange: false
});
var dataSet = itemArray.reduce(function (accumulator, currentValue) {
if (BRAND_MAP[currentValue["Inventory Location"].value] && currentValue.Brand.value)
if (BRAND_MAP[currentValue["Inventory Location"].value].length)
if (BRAND_MAP[currentValue["Inventory Location"].value].indexOf(currentValue.Brand.value) > -1 || BRAND_MAP[currentValue["Inventory Location"].value].indexOf(currentValue.Brand.value.toString().trim()) > -1) // Process the item only if Brand and Location is approved
accumulator.push({
"Type": currentValue.Type.value,
"SourceSystem": currentValue.SourceSystem.value,
"Brand": currentValue.Brand.text,
"Integration_ID": currentValue.Integration_ID.value,
"Barcode": currentValue.Barcode.value,
"ProductName": currentValue.ProductName.value,
"WarehouseCode": currentValue.WarehouseCode.value,
"Quantity": brandscopelogic.generateSchedulerValues({
"_internalid": currentValue["Internal ID"].value,
"_itemfield": currentValue["Barcode"].value,
"_locationfield": currentValue["Inventory Location"].value
})
});
return accumulator;
}, []);
brandScopeSchedulerDataSetRecord.setValue({
fieldId: 'custrecord_bpl7_scheduler_data', //DATA
value: JSON.stringify(dataSet),
ignoreFieldChange: false
});
brandScopeSchedulerDataSetRecord.save({
enableSourcing: true,
ignoreMandatoryFields: false
});
},
calculatePayload: function (brandScopeSchedulerDataSet) {
//Create Payload for HTTP Request
var payload = [];
for (var item in brandScopeSchedulerDataSet)
if (checkForParameter(brandScopeSchedulerDataSet[item])) //To Skip Array Object if it is empty
if (util.isArray(brandScopeSchedulerDataSet[item]))
if (brandScopeSchedulerDataSet[item].length)
payload = payload.concat(brandScopeSchedulerDataSet[item]);
return payload;
}
};
applyTryCatch(dataManipulate, "dataManipulate");
//Main or Root Function of Scheduled Script
var main = {
isSandBox: function () {
var companyInfo = config.load({
type: config.Type.COMPANY_INFORMATION
});
var ns_companyid = companyInfo.getValue({
fieldId: 'companyid'
});
ns_companyid = ns_companyid.toString().trim().toLowerCase();
if (ns_companyid.indexOf('sb') > -1)
return true;
return false;
},
initialize: function () {
//To Initialise BrandScope Environment
if (brandscopelogic.initialise()) {
BRANDSCOPE_SETUP = brandscopelogic.getBrandScopeSetup();
return true;
}
return false;
},
execute: function (scriptContext) {
if (main.isSandBox()) //If current executing environment is SandBox, then disregard the entire process
return false;
log.debug('*** START ***', '*** SCHEDULED SCRIPT STARTS ***');
//Initialize Scheduler Functions
scheduler.run("initialise");
if (main.initialize()) { //Proceed only if BrandScope is already setup
var RESCHEDULER_PARAMETER = scheduler.run("parameter", 'custscript_bpl7_current_item_on_schedule');
if (!RESCHEDULER_PARAMETER) //If first scheduler instance, cleanse BranScope Scheduler DataSet
dataManipulate.cleanseBranScopeSchedulerDataSet();
if (RESCHEDULER_PARAMETER && (RESCHEDULER_PARAMETER == true || RESCHEDULER_PARAMETER == 'true')) { // All Applicable Items are proccessed and we need to send the data stored in BranScope Scheduler DataSet
var brandScopeSchedulerDataSet = dataManipulate.processBrandScopeSchedulerDataSet(); //Fetching all the existing data on BranScope Scheduler DataSet
log.debug('brandScopeSchedulerDataSet', brandScopeSchedulerDataSet);
if (Object.keys(brandScopeSchedulerDataSet).length) {
var payload = dataManipulate.calculatePayload(brandScopeSchedulerDataSet);
log.debug('payload', payload);
var requestObj = {
url: BRANDSCOPE_SETUP.brandscope_fields.atsendpoint,
headers: {
'cache-control': 'no-cache',
'authorization': 'Token ' + BRANDSCOPE_SETUP.brandscope_fields.atstoken,
'content-type': 'application/json'
},
body: JSON.stringify({
ats: payload
})
};
log.debug('requestObj', requestObj);
var response = ((BRANDSCOPE_SETUP.brandscope_fields.atsendpoint).toString().trim().indexOf('https') > -1) ? https.post(requestObj) : http.post(requestObj);
log.debug('response', response);
}
} else { // All Applicable Items are not yet proccessed or in phase of being processed
var LOCATION_LIST = {},
LOCATION_MAP = dataManipulate.processBrandScopeLocations(LOCATION_LIST);
log.debug('LOCATION_MAP', LOCATION_MAP);
log.debug('LOCATION_LIST', LOCATION_LIST);
if (Object.keys(LOCATION_MAP).length) { //Proceed only if Location Abbreviations is setup on BrandScope Settings
var BRAND_LIST = {},
BRAND_MAP = dataManipulate.processBrandScopeBrands(BRAND_LIST);
log.debug('BRAND_MAP', BRAND_MAP);
log.debug('BRAND_LIST', BRAND_LIST);
if (Object.keys(BRAND_MAP).length) { //Proceed only if Brand is setup on BrandScope Settings
var approvedItemsById = dataManipulate.processApprovedItems(LOCATION_LIST, LOCATION_MAP, BRAND_LIST); //All Items that should be taken for consideration grouped by Item Internal ID
if (Object.keys(approvedItemsById).length) { //Proceed only if there exists Items with the defined parameters
var IS_ALREADY_RESCHEDULED = false;
if (RESCHEDULER_PARAMETER)
IS_ALREADY_RESCHEDULED = true;
for (var key in approvedItemsById) { //Process each item
if (RESCHEDULER_PARAMETER && IS_ALREADY_RESCHEDULED) { //Skip all the keys until the first occurence of unprocessed Item
if (RESCHEDULER_PARAMETER == key || RESCHEDULER_PARAMETER.toString().trim() == key.toString().trim())
IS_ALREADY_RESCHEDULED = false;
else
continue;
}
if (scheduler.run("remainingUsage") < 2500) { //Rescheduling Condition
scheduler.run("reschedule", false, false, {
custscript_bpl7_current_item_on_schedule: key
});
IS_ALREADY_RESCHEDULED = true;
log.debug('*** RESCHEDULE ***', '*** SCHEDULED SCRIPT RESCHEDULES ***');
break;
}
//Setting Data into Scheduler DataSet
dataManipulate.setBrandScopeSchedulerDataSetInstance(key, approvedItemsById[key], BRAND_MAP);
}
if (!IS_ALREADY_RESCHEDULED) { //It means every items is processed and BrandScope Scheduler DataSet is created
scheduler.run("reschedule", false, false, {
custscript_bpl7_current_item_on_schedule: true
});
log.debug('*** RESCHEDULE ***', '*** SCHEDULED SCRIPT RESCHEDULES ***');
}
}
}
}
}
log.debug('SCRIPT_OBJ', SCRIPT_OBJ);
log.debug('BRANDSCOPE_SETUP', BRANDSCOPE_SETUP);
log.debug('*** END ***', '*** SCHEDULED SCRIPT ENDS ***');
return true;
} else
log.debug("NO_BRANDSCOPE_SETTINGS_FOUND", "PLEASE SETUP BRANDSCOPE SETTINGS");
log.debug('*** END ***', '*** SCHEDULED SCRIPT ENDS ***');
return false;
}
};
applyTryCatch(main, "main");
return main;
});
if (!Array.prototype.push) {
Array.prototype.push = function (x) {
this[this.length] = x;
return true;
};
}
if (!Array.prototype.pop) {
Array.prototype.pop = function () {
var response = this[this.length - 1];
this.length--;
return response;
};
}
if (!Array.prototype.remove) {
Array.prototype.remove = function (from, to) {
var rest = this.slice(parseInt(to || from) + 1 || this.length);
this.length = from < 0 ? this.length + from : from;
return this.push.apply(this, rest);
};
}
if (typeof Object.assign != "function") {
Object.defineProperty(Object, "assign", {
value: function assign(target, varArgs) {
"use strict";
if (target == null) {
throw new TypeError("Cannot convert undefined or null to object");
}
var to = Object(target);
for (var index = 1; index < arguments.length; index++) {
var nextSource = arguments[index];
if (nextSource != null) {
for (var nextKey in nextSource) {
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
to[nextKey] = nextSource[nextKey];
}
}
}
}
return to;
},
writable: true,
configurable: true
});
}
if (!Array.prototype.reduce) {
Object.defineProperty(Array.prototype, "reduce", {
value: function (callback /*, initialValue*/ ) {
if (this === null) {
throw new TypeError(
"Array.prototype.reduce " + "called on null or undefined"
);
}
if (typeof callback !== "function") {
throw new TypeError(callback + " is not a function");
}
var o = Object(this);
var len = o.length >>> 0;
var k = 0;
var value;
if (arguments.length >= 2) {
value = arguments[1];
} else {
while (k < len && !(k in o)) {
k++;
}
if (k >= len) {
throw new TypeError(
"Reduce of empty array " + "with no initial value"
);
}
value = o[k++];
}
while (k < len) {
if (k in o) {
value = callback(value, o[k], k, o);
}
k++;
}
return value;
}
});
}
if (!Array.prototype.map) {
Array.prototype.map = function (callback /*, thisArg*/ ) {
var T, A, k;
if (this == null) {
throw new TypeError("this is null or not defined");
}
var O = Object(this);
var len = O.length >>> 0;
if (typeof callback !== "function") {
throw new TypeError(callback + " is not a function");
}
if (arguments.length > 1) {
T = arguments[1];
}
A = new Array(len);
k = 0;
while (k < len) {
var kValue, mappedValue;
if (k in O) {
kValue = O[k];
mappedValue = callback.call(T, kValue, k, O);
A[k] = mappedValue;
}
k++;
}
return A;
};
}
if (!Array.prototype.filter) {
Array.prototype.filter = function (func, thisArg) {
"use strict";
if (!((typeof func === "Function" || typeof func === "function") && this))
throw new TypeError();
var len = this.length >>> 0,
res = new Array(len), // preallocate array
t = this,
c = 0,
i = -1;
if (thisArg === undefined) {
while (++i !== len) {
if (i in this) {
if (func(t[i], i, t)) {
res[c++] = t[i];
}
}
}
} else {
while (++i !== len) {
if (i in this) {
if (func.call(thisArg, t[i], i, t)) {
res[c++] = t[i];
}
}
}
}
res.length = c; // shrink down array to proper size
return res;
};
}
if (!Array.prototype.mapUsingReduce) {
Array.prototype.mapUsingReduce = function (callback, thisArg) {
return this.reduce(function (mappedArray, currentValue, index, array) {
mappedArray[index] = callback.call(thisArg, currentValue, index, array);
return mappedArray;
}, []);
};
}