JIRA Code : MICL-300
/**
* @NApiVersion 2.1
* @NScriptType Restlet
*/
/*************************************************************************************************************************
* Madi International Co LLC-UAE-SCA
*
* ${MICL-358} : ${Netsuite Endpoint for Item Fulfilment exposed for Maersk Integration: Pack Confirmation}
*********************************************************************************************************************
*
* Author: Jobin & Jismi IT Services
*
* Date Created : 11-April-2023
*
* Description :This Restlet script is used to create the Item fulfillment with Packed status from Maersk to NetSuite
*
* REVISION HISTORY
*
* @version 1.0 MICL-358 : 11-April-2023 : Created the initial build by JJ0170
* @version 2.0 MICL-359 : 18-April-2023 : Netsuite Endpoint for Item Fulfilment exposed for Maersk Integration: Ship Confirmation
* @version 3.0 MICL-416 : 28-April-2023 : Netsuite Endpoint for Item Fulfilment exposed for Maersk Integration for Transfer Order : Pack Confirmation
**************************************************************************************************************************/
define(['N/record', 'N/search', 'N/config', '../../Common Library/JJ Maersk NS Utility.js', '../../Config Module/JJ CM Maersk API Configuration.js', 'N/util'],
/**
* @param{record} record
* @param{search} search
* @param{config} config
* @param{maerskUtility} maerskUtility
* @param{maerskConfig} maerskConfig
* @param{util} util
*/
(record, search, config, maerskUtility, maerskConfig, util) => {
/** Dataset containing different Saved searches **/
const DATASETS = {
/**
* Function to search for the Maersk integration record associated with the Extenal Document Number
* @param {string} externalDocumentNumber - External Document number from Request body
* @returns {object} maerskInfo - Object contains maersk Integration Info details
*/
fetchIntegrationRecord(externalDocumentNumber) {
try {
let customrecord_jj_cr_maersk_integrtn_infoSearchObj = search.create({
type: "customrecord_jj_cr_maersk_integrtn_info",
filters:
[
["custrecord_jj_transaction_micl300.numbertext", "is", externalDocumentNumber]
],
columns:
[
search.createColumn({ name: "internalid", label: "Internal ID" }),
search.createColumn({ name: "type", join: "CUSTRECORD_JJ_TRANSACTION_MICL300", label: "Type" }),
search.createColumn({ name: "internalid", join: "CUSTRECORD_JJ_TRANSACTION_MICL300", label: "Internal ID" }),
search.createColumn({ name: "statusref", join: "CUSTRECORD_JJ_TRANSACTION_MICL300", label: "Status" }),
search.createColumn({ name: "custrecord_jj_so_sync_micl300", label: "JJ Maersk SO Sync to Maersk" })
]
});
let searchResultCount = customrecord_jj_cr_maersk_integrtn_infoSearchObj.runPaged().count;
let maerskInfo = {};
if (searchResultCount > 0) {
customrecord_jj_cr_maersk_integrtn_infoSearchObj.run().each(function (result) {
maerskInfo.recordInternalId = result.getValue({ name: "internalid", label: "Internal ID" });
maerskInfo.type = result.getValue({ name: "type", join: "CUSTRECORD_JJ_TRANSACTION_MICL300", label: "Type" });
maerskInfo.internalId = result.getValue({ name: "internalid", join: "CUSTRECORD_JJ_TRANSACTION_MICL300", label: "Internal ID" });
maerskInfo.status = result.getValue({ name: "statusref", join: "CUSTRECORD_JJ_TRANSACTION_MICL300", label: "Status" });
maerskInfo.maerskSyncCheck = result.getValue({ name: "custrecord_jj_so_sync_micl300", label: "JJ Maersk SO Sync to Maersk" });
});
return maerskInfo;
}
else {
return false;
}
}
catch (e) {
log.error("Error@fetchIntegrationRecord", e.name);
return {};
}
},
/**
* Function to search for the Netuite Location ID using the Maersk Plant Id
* @param {string} maerskPlantId - Plant Id from the JSON request body
* @returns {number} locationId - Internal ID of the synced NetSuite Location
*/
fetchLocation(maerskPlantId) {
try {
let customrecord_jj_cr_loc_map_micl300SearchObj = search.create({
type: "customrecord_jj_cr_loc_map_micl300",
filters:
[
["custrecord_jj_maersk_plantid_micl300", "is", maerskPlantId],
"AND",
["isinactive", "is", "F"]
],
columns:
[
search.createColumn({ name: "custrecord_jj_netsuite_location_micl300", label: "JJ NetSuite Location" })
]
});
let searchResultCount = customrecord_jj_cr_loc_map_micl300SearchObj.runPaged().count, locationId;
if (searchResultCount > 0) {
customrecord_jj_cr_loc_map_micl300SearchObj.run().each(function (result) {
locationId = result.getValue({ name: "custrecord_jj_netsuite_location_micl300", label: "JJ NetSuite Location" });
});
}
return locationId;
}
catch (e) {
log.error("Error@fetchLocation", e.name);
return false;
}
},
/**
* Function to search for the order details based on type
* @param {String} documentNumber - Document number of the transaction
* @param {String} type - Type of transaction
* @returns {Object} finalDataObj - object contains transaction details
*/
fetchOrderDetails(documentNumber, type) {
try {
let filter = [
["mainline", "is", "F"],
"AND",
["shipping", "is", "F"],
"AND",
["taxline", "is", "F"],
"AND",
["numbertext", "is", documentNumber]
];
let column = [
search.createColumn({ name: "item", label: "item" }),
search.createColumn({ name: "itemid", join: "item", label: "itemId" }),
search.createColumn({ name: "line", label: "lineId" }),
search.createColumn({ name: "custitem_jj_maersk_item_sync_micl300", join: "item", label: "itemSyncCheck" }),
search.createColumn({ name: "closed", label: "closed" }),
];
if (type == "SalesOrd") {
type = "salesorder";
filter.push("AND", ["type", "anyof", "SalesOrd"]);
column.push(
search.createColumn({ name: "quantity", label: "quantity" }),
search.createColumn({ name: "formulanumeric", formula: "NVL({quantitypicked},0)", label: "pickedQuantity" }),
search.createColumn({ name: "formulanumeric", formula: "NVL({quantitycommitted},0)-NVL({quantitypicked},0)", label: "availableQuantity" })
);
}
if (type == "TrnfrOrd") {
type = "transferorder";
filter.push("AND", ["type", "anyof", "TrnfrOrd"], "AND", ["transactionlinetype", "anyof", "ITEM"]);
column.push(
search.createColumn({ name: "formulanumeric", formula: "ABS({quantity})", label: "quantity" }),
search.createColumn({ name: "formulanumeric", formula: "NVL({transferorderquantitypicked},0)", label: "pickedQuantity" }),
search.createColumn({ name: "formulanumeric", formula: "NVL({transferorderquantitycommitted},0)-NVL({transferorderquantitypicked},0)", label: "availableQuantity" })
);
}
if (type == "VendAuth") {
type = "vendorreturnauthorization";
filter.push("AND", ["type", "anyof", "VendAuth"]);
column.push(
search.createColumn({ name: "formulanumeric", formula: "ABS({quantity})", label: "quantity" }),
search.createColumn({ name: "formulanumeric", formula: "NVL({transferorderquantitypicked},0)", label: "pickedQuantity" }),
search.createColumn({ name: "formulanumeric", formula: "NVL({transferorderquantitycommitted},0)-NVL({transferorderquantitypicked},0)", label: "availableQuantity" })
);
}
let transactionSearchObj = search.create({
type: type,
filters: filter,
columns: column
});
let searchResultCount = transactionSearchObj.runPaged().count;
let finalDataObj = {};
if (searchResultCount > 0) {
transactionSearchObj.run().each(function (result) {
let singleDataObj = {};
let lineId = result.getValue({ name: "line", label: "lineId" });
for (let i = 0; i < transactionSearchObj.columns.length; i++) {
singleDataObj[transactionSearchObj.columns[i].label] = {
value: result.getValue(transactionSearchObj.columns[i]),
text: result.getText(transactionSearchObj.columns[i])
}
}
finalDataObj[lineId] = singleDataObj;
return true;
});
}
return finalDataObj;
} catch (e) {
log.error("Error @fetchOrderDetails", e);
return false;
}
},
/**
* Function to search whether the IF has been synced with NetSuite
* @param {number} itemFulfillmentNumber - Internal ID of the Item Fulfillment
* @returns {object | boolean} itemFulfillmentInfo/ false - object contains IF information/ false
*/
fetchItemFulfillment(itemFulfillmentNumber) {
try {
let itemfulfillmentSearchObj = search.create({
type: "itemfulfillment",
filters:
[
["type", "anyof", "ItemShip"],
"AND",
["internalid", "anyof", Number(itemFulfillmentNumber)],
"AND",
["mainline", "is", "T"],
],
columns:
[
search.createColumn({ name: "statusref", label: "Status" }),
search.createColumn({ name: "custbody_jj_maersk_if_sync_micl300", label: "Maersk IF Sync " })
]
});
let searchResultCount = itemfulfillmentSearchObj.runPaged().count;
let itemFulfillmentInfo = {};
if (searchResultCount > 0) {
itemfulfillmentSearchObj.run().each(function (result) {
itemFulfillmentInfo.ifStatus = result.getValue({ name: "statusref", label: "Status" });
itemFulfillmentInfo.maerskIfSync = result.getValue({ name: "custbody_jj_maersk_if_sync_micl300", label: "Maersk IF Sync " })
});
return itemFulfillmentInfo;
}
else {
return {};
}
}
catch (e) {
log.error("Error@checkItemFulfillment", e.name);
return {};
}
},
}
/** common utility and config functions */
const PROCESS = {
/**
* Function to set the JSON response
* @param {object} responseObject - object that contains status and message
* @returns {string} - JSON response
*/
jsonResponse(responseObject) {
try {
let jsonResponse = {
summary: {
status: responseObject.status,
message: responseObject.message
}
}
if (responseObject.status == "SUCCESS") {
jsonResponse.result = responseObject.result
}
return JSON.stringify(jsonResponse);
}
catch (e) {
log.error("Error@jsonResponse", e);
return JSON.stringify({
summary: {
status: responseObject.status,
message: responseObject.message
}
});
}
},
/**
* Function to process the Pack Request Body from Maersk
* @param {object} PackRequest - Request body of Pack confirmation
* @returns {string | object} - Returns the response object contains status and message
*/
validatePackRequest(PackRequest) {
let responseObj = { status: "", message: "" };
try {
// Empty Request Validation
if (!maerskUtility.checkForParameter(PackRequest)) {
responseObj.status = "FAILURE";
responseObj.message = "EMPTY_PACK_REQUEST";
}
else {
// Validation for Plant Id
if (!maerskUtility.checkForParameter(PackRequest?.Request.Sales_Order_Pack.PlantId)) {
responseObj.status = "FAILURE";
responseObj.message = "PARAMETER_IS_EMPTY : PlantId";
}
// Validation for External Document Id
else if (!maerskUtility.checkForParameter(PackRequest?.Request.Sales_Order_Pack.ExternDocId)) {
responseObj.status = "FAILURE";
responseObj.message = "PARAMETER_IS_EMPTY : ExternDocId";
}
// Validation for Final flag
else if (
!maerskUtility.checkForParameter(PackRequest?.Request.Sales_Order_Pack.FinalFlag) ||
!(PackRequest?.Request.Sales_Order_Pack.FinalFlag == "0" || PackRequest?.Request.Sales_Order_Pack.FinalFlag == "1")) {
responseObj.status = "FAILURE";
responseObj.message = "PARAMETER_IS_EMPTY_OR_INVALID :FinalFlag ";
}
// Validation for Total Lines
else if (
!maerskUtility.checkForParameter(PackRequest?.Request.Sales_Order_Pack.TotalLines) ||
!Number(PackRequest?.Request.Sales_Order_Pack.TotalLines) > 0) {
responseObj.status = "FAILURE";
responseObj.message = "PARAMETER_IS_EMPTY_OR_INVALID : TotalLines";
}
// Validation for Current Request
else if (
!maerskUtility.checkForParameter(PackRequest?.Request.Sales_Order_Pack.CurrReq) ||
!Number(PackRequest?.Request.Sales_Order_Pack.CurrReq) > 0
) {
responseObj.status = "FAILURE";
responseObj.message = "PARAMETER_IS_EMPTY_OR_INVALID : CurrentReq";
}
// Validation for Total Left
else if (
!maerskUtility.checkForParameter(PackRequest?.Request.Sales_Order_Pack.TotalLeft) ||
!(PackRequest?.Request.Sales_Order_Pack.TotalLeft == "0" || Number(PackRequest?.Request.Sales_Order_Pack.TotalLeft) > 0)
) {
responseObj.status = "FAILURE";
responseObj.message = "PARAMETER_IS_EMPTY_OR_INVALID : TotalLeft";
}
// Validation for Item Lines
else {
for (let i = 0; i < PackRequest?.Request.Sales_Order_Pack.PackDetailInfo.ItemInfo.length; i++) {
// ExternLineNo
if (
!maerskUtility.checkForParameter(PackRequest?.Request.Sales_Order_Pack.PackDetailInfo.ItemInfo[i].ExternLineNo) ||
!Number(PackRequest?.Request.Sales_Order_Pack.PackDetailInfo.ItemInfo[i].ExternLineNo) > 0
) {
responseObj.status = "FAILURE";
responseObj.message = "PARAMETER_IS_EMPTY_OR_INVALID : ExternLineNo at Line:" + (i + 1);
break;
}
//ItemId
else if (!maerskUtility.checkForParameter(PackRequest?.Request.Sales_Order_Pack.PackDetailInfo.ItemInfo[i].ItemId)) {
responseObj.status = "FAILURE";
responseObj.message = "PARAMETER_IS_EMPTY : ItemId at Line:" + (i + 1);
break;
}
// ItemQty
else if (
!maerskUtility.checkForParameter(PackRequest?.Request.Sales_Order_Pack.PackDetailInfo.ItemInfo[i].ItemQty)
|| !Number(PackRequest?.Request.Sales_Order_Pack.PackDetailInfo.ItemInfo[i].ItemQty) > 0
) {
responseObj.status = "FAILURE";
responseObj.message = "PARAMETER_IS_EMPTY_OR_INVALID : ItemQty at Line:" + (i + 1);
break;
}
}
}
}
return responseObj;
}
catch (e) {
log.error("Error@validatePackRequest", e.name);
responseObj.status = "ERROR";
responseObj.message = "INVALID_PACK_REQUEST"
return responseObj;
}
},
/**
* Function to process the Ship Request Body from Maersk
* @param {object} shipRequest - Request body of Ship confirmation
* @returns {string | object} - Returns the response object contains status and message
*/
validateShipRequest(shipRequest) {
let responseObj = { status: "", message: "" };
try {
// Empty Request Validation
if (!util.isObject(shipRequest)){
responseObj.status = "FAILURE";
responseObj.message = "EMPTY_SHIP_REQUEST";
}
// Validation for Item Fulfillment ID
else if (!maerskUtility.checkForParameter(shipRequest?.Request.itemFulfillmentNumber)) {
responseObj.status = "FAILURE";
responseObj.message = "PARAMETER_IS_EMPTY : itemFulfillmentNumber";
}
// Validation for Item Fulfillment Document Number
else if (!maerskUtility.checkForParameter(shipRequest?.Request.itemFulfillmentDocNumber)) {
responseObj.status = "FAILURE";
responseObj.message = "PARAMETER_IS_EMPTY : itemFulfillmentDocNumber";
}
return responseObj;
}
catch (e) {
log.error("Error@validateShipRequest", e);
responseObj.status = "ERROR";
responseObj.message = e.message
return responseObj;
}
},
/**
* Function to check the item lines and return the json response
* @param {Object} jsonRequest - JSON data sent from the Maersk
* @param {Object} intgrationInstance - Integration record detail object
* @returns {Object} responseObj - Object containg status and message
*/
itemLineJsonParameterChecking(jsonRequest, intgrationInstance) {
let responseObj = { status: "", message: "" };
try {
let orderDetails = DATASETS.fetchOrderDetails(jsonRequest.Request.Sales_Order_Pack.ExternDocId, intgrationInstance.type);
let itemInfo = jsonRequest.Request.Sales_Order_Pack.PackDetailInfo.ItemInfo;
let nsItemId = [];
for (let i = 0; i < itemInfo.length; i++) {
if (orderDetails[itemInfo[i].ExternLineNo]) {
responseObj.status = "true";
// Splitting the item name with colon for items having name as parent:child
nsItemId = orderDetails[itemInfo[i].ExternLineNo].itemId.value.split(/: /);
if (nsItemId.length > 1) {
nsItemId = nsItemId[nsItemId.length - 1]
}
// check for item Id
if (nsItemId == itemInfo[i].ItemId) {
responseObj.status = "true";
// check for closed lines
if (!orderDetails[itemInfo[i].ExternLineNo].closed.value) {
responseObj.status = "true";
// When Pack Qty is greater then NS qty
if (Number(itemInfo[i].ItemQty) <= Number(orderDetails[itemInfo[i].ExternLineNo].quantity.value)) {
responseObj.status = "true";
// When there is no qty to fulfill in NS
if (
(Number(orderDetails[itemInfo[i].ExternLineNo].quantity.value) - Number(orderDetails[itemInfo[i].ExternLineNo].pickedQuantity.value)) >= Number(itemInfo[i].ItemQty)) {
responseObj.status = "true";
}
else {
responseObj.status = "false";
responseObj.message = "QUANTITY_NOT_AVAILABLE_FOR_FULFILLING_ITEM " + itemInfo[i].ItemId + " AT_LINE : " + (i + 1);
break;
}
}
else {
responseObj.status = "false";
responseObj.message = "PACK_QUANTITY_IS_GREATER_THAN_ORDER_QUANTITY FOR_ITEM " + itemInfo[i].ItemId + " LINE : " + (i + 1);
break;
}
} else {
responseObj.status = "false";
responseObj.message = "ITEM_IS_CLOSED_IN_RELATED_TRANSACTION : " + itemInfo[i].ItemId
break;
}
} else {
responseObj.status = "false";
responseObj.message = "ITEM " + itemInfo[i].ItemId + " NOT_FOUND_AT_LINE : " + (i + 1);
break;
}
}
else {
responseObj.status = "false";
responseObj.message = "ITEM_LINE_NUMBER_NOT_FOUND_IN_RELATED_TRANSACTION : " + itemInfo[i].ExternLineNo
break;
}
}
return responseObj;
} catch (e) {
log.error("Error@itemLineJsonParameterChecking", e);
responseObj.status = "SYSTEM ERROR"
responseObj.message = "RECORD_NOT_CREATED"
return responseObj;
}
},
/**
* Function to process the paginated JSON request to create the Item Fulfillment
* @param {Object} jsonRequest - JSON Request object containg item fulfillment (pack) details
* @returns {Object} responseObj - response JSON object
*/
processPackData(jsonRequest) {
let responseObj = {};
try {
let itemFulfillmentInfo;
let externalDocumentNumber = jsonRequest.Request.Sales_Order_Pack.ExternDocId;
let integrationData = DATASETS.fetchIntegrationRecord(externalDocumentNumber);
let locationId = DATASETS.fetchLocation(jsonRequest.Request.Sales_Order_Pack.PlantId);
// Continue when the Sales order is synced with Maersk
if (Object.entries(integrationData).length !== 0 && integrationData.maerskSyncCheck && maerskUtility.checkForParameter(locationId)) {
// Comparing item lines in Pack request and the transaction data
let itemLineCheck = PROCESS.itemLineJsonParameterChecking(jsonRequest, integrationData);
if (!itemLineCheck.message) {
// Continue when the sales order is in Pending/Partiallly Fulfilled status
if (
integrationData.status == "pendingFulfillment" ||
integrationData.status == "pendingBillingPartFulfilled" ||
integrationData.status == "pendingReceiptPartFulfilled" ||
integrationData.status == "pendingReturn" ||
integrationData.status == "pendingCreditPartReturned"
) {
// Continue when the final flag is true (last request for SO or TO)
if (
jsonRequest.Request.Sales_Order_Pack.FinalFlag == "1" &&
jsonRequest.Request.Sales_Order_Pack.TotalLines == jsonRequest.Request.Sales_Order_Pack.CurrReq
) {
itemFulfillmentInfo = PROCESS.createItemFulfillment(jsonRequest, integrationData.type, integrationData.internalId, integrationData.recordInternalId, locationId);
if (itemFulfillmentInfo.itemFulfillmentId) {
responseObj.status = "SUCCESS"
responseObj.message = "ITEM_FULFILLMENT_CREATED",
responseObj.result = {
"itemFulfillmentNumber": itemFulfillmentInfo.itemFulfillmentId,
"itemFulfillmentDocNumber": itemFulfillmentInfo.itemFulfillmentDocNum
}
}
else {
responseObj.status = itemFulfillmentInfo.status
responseObj.message = itemFulfillmentInfo.message
}
}
// To process multiple request for Item fulfillment
else {
let updateCustomRecord = PROCESS.updateCustomRecordInstance(integrationData.recordInternalId, jsonRequest);
// Continue when Total left is Zero
if (jsonRequest.Request.Sales_Order_Pack.TotalLeft == "0") {
itemFulfillmentInfo = PROCESS.createItemFulfillment(JSON.parse(updateCustomRecord), integrationData.type, integrationData.internalId, integrationData.recordInternalId, locationId);
if (itemFulfillmentInfo.itemFulfillmentId) {
responseObj.status = "SUCCESS";
responseObj.message = "ITEM_FULFILLMENT_CREATED";
responseObj.result = {
"itemReceiptNumber": itemFulfillmentInfo.itemFulfillmentId,
"itemReceiptDocNumber": itemFulfillmentInfo.itemFulfillmentId
};
}
else {
responseObj.status = itemFulfillmentInfo.status
responseObj.message = itemFulfillmentInfo.message
}
}
// Total Left is not Zero
else {
if (maerskUtility.checkForParameter(updateCustomRecord)) {
responseObj.status = "SUCCESS"
responseObj.message = "PAGINATED_PAYLOAD"
responseObj.result = {
"customItemFulfillmentID": integrationData.recordInternalId,
}
return responseObj;
} else {
responseObj.status = "FAILURE"
responseObj.message = "PAGINATED_PAYLOAD_NOT_UPDATED"
}
}
}
}
else {
responseObj.status = "FAILURE"
responseObj.message = {
reason: "INVALID_ORDER_STATUS_FOR_IF_CREATION",
transactionStatus: integrationData.status
}
}
}
else {
responseObj.status = "FAILURE";
responseObj.message = itemLineCheck.message;
}
}
else {
responseObj.status = "FAILURE";
responseObj.message = "RELATED_ORDER_IS_NOT_SYNCED_TO_MAERSK";
}
return responseObj;
}
catch (e) {
log.error("Error@processPackData", e.name);
responseObj.status = "SYSTEM ERROR"
responseObj.message = "RECORD_NOT_CREATED"
return responseObj;
}
},
/**
* Function to process the paginated JSON request to update the Item Fulfillment with Shipped status
* @param {Object} jsonRequest - JSON Request object containg item fulfillment (ship) details
* @returns {Object} responseObj - response JSON object
*/
processShipData(jsonRequest) {
let responseObj = {};
try {
// Searching wheter the IF has been already synced
let itemShipInfo = DATASETS.fetchItemFulfillment(jsonRequest.Request.itemFulfillmentNumber);
if ( Object.keys(itemShipInfo).length == 0 ) {
responseObj.status = "ERROR"
responseObj.message = "ITEM_FULFILLMENT_NOT_EXIST"
}
else if (itemShipInfo.maerskIfSync == false) {
responseObj.status = "ERROR"
responseObj.message = "PACK_DATA_NOT_SYNCED"
}
else if (maerskUtility.checkForParameter(itemShipInfo.ifStatus) && itemShipInfo.ifStatus == "shipped") {
responseObj.status = "ERROR"
responseObj.message = "ITEM_FULFILLMENT_ALREADY_SHIPPED"
}
else {
// Setting status as Shipped
let updateIfStatus ;
try{
updateIfStatus = record.submitFields({
type: record.Type.ITEM_FULFILLMENT,
id: Number(jsonRequest.Request.itemFulfillmentNumber),
values: { 'shipstatus': "C" },
options : {
enableSourceing : false,
ignoreMandatoryFields : true
}
});
}
catch(e){
log.error("Error @ record.SubmitFields", e);
}
if (maerskUtility.checkForParameter(updateIfStatus)) {
responseObj.status = "SUCCESS"
responseObj.message = "ITEM_FULFILLMENT_SHIPPED",
responseObj.result = {
"itemFulfillmentNumber": jsonRequest.Request.itemFulfillmentNumber,
"itemFulfillmentDocNumber": jsonRequest.Request.itemFulfillmentDocNumber
}
}
else {
responseObj.status = "ERROR"
responseObj.message = "RECORD_NOT_UPDATED"
}
}
return responseObj;
}
catch (e) {
log.error("Error@processShipData", e);
responseObj.status = "ERROR"
responseObj.message = e.message
return responseObj;
}
},
/**
* Function to create the object with key values as external so line number for mapping.
* @param {Array} fulfillmentItemInfo - Array of object contains item line details
* @returns {Object} finalDataObj - Object of item lines from the JSON data
*/
finalFulfillmentProcessData(fulfillmentItemInfo) {
try {
let finalDataObj = {};
for (let i = 0; i < fulfillmentItemInfo.length; i++) {
let singleDataObj = {}
singleDataObj.ExternLineNo = fulfillmentItemInfo[i].ExternLineNo;
singleDataObj.ItemId = fulfillmentItemInfo[i].ItemId;
singleDataObj.ItemQty = fulfillmentItemInfo[i].ItemQty;
finalDataObj[singleDataObj.ExternLineNo] = singleDataObj;
}
return finalDataObj;
}
catch (e) {
log.error("Error@finalFulfillmentProcessData", e.name);
return {};
}
},
/**
* Function to update the integration record instance with JSON request body sent from Maersk for IF creation
* @param {String} maerskIntegRecId - Maersk integration record Internal ID
* @param {Object} jsonRequest - JSON object contains item fulfillment information
* @returns {String} updatedJsonFulfillmentLines - updated JSON
*/
updateCustomRecordInstance(maerskIntegRecId, jsonRequest) {
try {
let updatedJsonFulfillmentLines;
let existingPayload = search.lookupFields({
type: "customrecord_jj_cr_maersk_integrtn_info",
id: maerskIntegRecId,
columns: ['custrecord_jj_maersk_if_json_micl300']
}).custrecord_jj_maersk_if_json_micl300;
if (existingPayload) {
updatedJsonFulfillmentLines = PROCESS.updateExistingJsonDataItemLines(existingPayload, jsonRequest);
} else {
updatedJsonFulfillmentLines = JSON.stringify(jsonRequest);
}
record.submitFields({
type: 'customrecord_jj_cr_maersk_integrtn_info',
id: maerskIntegRecId,
values: {
'custrecord_jj_maersk_if_json_micl300': updatedJsonFulfillmentLines
}
});
return updatedJsonFulfillmentLines;
} catch (e) {
log.error("Error@updateCustomRecordInstance", e.name);
PROCESS.storeError(maerskIntegRecId, e.message);
return false;
}
},
/**
* Function to update the existing JSON line items with new JSON line items for a paginated request
* @param {String} existingPayload - JSON data already saved in the custom record
* @param {Object} jsonRequest - Received JSON data for IF creation
* @returns {Object} existingPayload - stringified object for updating the custom record instance
*/
updateExistingJsonDataItemLines(existingPayload, jsonRequest) {
try {
existingPayload = JSON.parse(existingPayload);
let nextItemLines = jsonRequest.Request.Sales_Order_Pack.PackDetailInfo.ItemInfo;
let finalDataArray = [existingPayload.Request.Sales_Order_Pack.PackDetailInfo.ItemInfo];
finalDataArray.push(nextItemLines);
existingPayload.Request.Sales_Order_Pack.PackDetailInfo.ItemInfo = finalDataArray.flat(Infinity); // to combine multiple array in to single array
return JSON.stringify(existingPayload);
}
catch (e) {
log.error("Error@updateExistingJsonDataItemLines", e.name);
return false;
}
},
/**
* Function to sum fulfilled quantity by referring the ExternalLineNo in the pack request
* @param {object} requestBody - JSON request body contains pack confirmation details
* @return {object} requestBody
*/
appendPackItemsInSameLineId(requestBody) {
try {
let itemDetails = requestBody.Request.Sales_Order_Pack.PackDetailInfo.ItemInfo;
let finalObj = {}
for (let i = 0; i < itemDetails.length; i++) {
if (finalObj[itemDetails[i].ExternLineNo]) {
finalObj[itemDetails[i].ExternLineNo].ItemQty = Number(finalObj[itemDetails[i].ExternLineNo].ItemQty) + Number(itemDetails[i].ItemQty)
} else {
finalObj[itemDetails[i].ExternLineNo] = itemDetails[i]
}
}
let finalArray = []
for (let key in finalObj) {
finalArray.push(finalObj[key]);
}
requestBody.Request.Sales_Order_Pack.PackDetailInfo.ItemInfo = finalArray;
return requestBody;
} catch (e) {
log.error("Error @appendPackItemsInSameLineId", e);
return false;
}
},
/**
*Function to create Item fulfillment based on the transaction type (Sales order/Transfer Order)
* @param {Object} jsonRequest - JSON request body
* @param {String} orderType - Type of the transaction
* @param {number} transactionId - Internal Id of the transaction
* @param {number} customRecordId - Internal Id of the custom record
* @param {number} locationId - Internal Id of the NetSuite location
* @returns {Object} - Object contains IF Internal ID and IF Document Number
*/
createItemFulfillment(jsonRequest, orderType, transactionId, customRecordId, locationId) {
let returnObj = {};
try {
// converting array of objects to objects of object (ItemInfo of Pack request)
let finalDataToFulfill = PROCESS.finalFulfillmentProcessData(jsonRequest.Request.Sales_Order_Pack.PackDetailInfo.ItemInfo);
let ifRec, fulfillCheck = true, available;
if (orderType == "SalesOrd" || orderType == "TrnfrOrd") {
let checkCommitment = DATASETS.fetchOrderDetails(jsonRequest.Request.Sales_Order_Pack.ExternDocId, orderType);
if (Object.entries(finalDataToFulfill).length > 0) {
for (let key in finalDataToFulfill) {
let nsItemLine = checkCommitment[key];
if (nsItemLine.availableQuantity.value < finalDataToFulfill[key].ItemQty) {
available = false;
break;
}
else {
available = true;
}
}
}
// Sales order into Item Fulfillment
if (orderType == "SalesOrd") {
if (available == true) { //When commitment available
ifRec = record.transform({
fromType: record.Type.SALES_ORDER,
fromId: transactionId,
toType: record.Type.ITEM_FULFILLMENT,
defaultValues: {
inventorylocation: Number(locationId)
}
});
}
else {
ifRec = record.transform({
fromType: record.Type.SALES_ORDER,
fromId: transactionId,
toType: record.Type.ITEM_FULFILLMENT,
});
}
}
// Transfer Order into Item Fulfillment
else if (orderType == "TrnfrOrd") {
if (available == true) { //When commitment available
ifRec = record.transform({
fromType: record.Type.TRANSFER_ORDER,
fromId: transactionId,
toType: record.Type.ITEM_FULFILLMENT,
defaultValues: {
inventorylocation: Number(locationId)
}
});
}
else {
ifRec = record.transform({
fromType: record.Type.TRANSFER_ORDER,
fromId: transactionId,
toType: record.Type.ITEM_FULFILLMENT,
});
}
}
}
if (orderType == "VendAuth") {
// Vendor return authorization into Item Fulfillment
ifRec = record.transform({
fromType: record.Type.VENDOR_RETURN_AUTHORIZATION,
fromId: transactionId,
toType: record.Type.ITEM_FULFILLMENT,
});
}
// Setting status as Packed
ifRec.setValue({ fieldId: "shipstatus", value: "B" });
let itemCount = ifRec.getLineCount({ sublistId: 'item' });
for (let i = 0; i < itemCount; i++) {
let itemLine = ifRec.getSublistValue({
sublistId: 'item',
fieldId: 'orderline',
line: i
});
let itemId = ifRec.getSublistText({
sublistId: 'item',
fieldId: 'itemname',
line: i
});
let itemQuantity = ifRec.getSublistValue({
sublistId: 'item',
fieldId: 'quantity',
line: i
});
if (orderType == "TrnfrOrd") {
itemLine = itemLine - 1;
}
// When item lines are available in NetSuite
if (finalDataToFulfill[itemLine]) {
if (itemId == finalDataToFulfill[itemLine].ItemId) {
if (finalDataToFulfill[itemLine].ItemQty <= itemQuantity) {
ifRec.setSublistValue({
sublistId: 'item',
fieldId: 'quantity',
value: finalDataToFulfill[itemLine].ItemQty,
line: i
});
} else {
fulfillCheck = false;
returnObj.status = "FAILUE";
returnObj.message = "PACK_QUANTITY_IS_GREATER_THAN_ORDER_QUANTITY_AT_LINE :" + (i + 1) + " FOR_ITEM " + itemId;
break;
}
} else {
receiveCheck = false;
returnObj.status = "FAILUE";
returnObj.message = "ITEM_NOT_FOUND_IN_RELATED_TRANSACTION_AT_LINE :" + (i + 1);
break;
}
}
else {
ifRec.setSublistValue({
sublistId: 'item',
fieldId: 'itemreceive',
value: false,
line: i
});
}
}
// Save Item Fulfillment when above conditions satisfied
if (fulfillCheck) {
returnObj.itemFulfillmentId = ifRec.save({
enableSourceing: true,
ignoreMandatoryFields: true
});
// Fetch Item Fulfillment Document Number
returnObj.itemFulfillmentDocNum = search.lookupFields({
type: "itemfulfillment",
id: returnObj.itemFulfillmentId,
columns: ["tranid"]
}).tranid;
// Check for Multiple Item Fulfillments for the current order
if (returnObj.itemFulfillmentId) {
let customRecordObj = search.lookupFields({
type: "customrecord_jj_cr_maersk_integrtn_info",
id: customRecordId.toString(),
columns: ["custrecord_jj_item_fulfillment_micl300"]
});
let itemFulfillmentArray = [];
if (
maerskUtility.checkForParameter(customRecordObj["custrecord_jj_item_fulfillment_micl300"]) &&
customRecordObj["custrecord_jj_item_fulfillment_micl300"].length != 0
) {
for (let i = 0; i < customRecordObj["custrecord_jj_item_fulfillment_micl300"].length; i++) {
itemFulfillmentArray.push(customRecordObj["custrecord_jj_item_fulfillment_micl300"][i].value);
}
itemFulfillmentArray.push(returnObj.itemFulfillmentId);
}
else {
itemFulfillmentArray.push(returnObj.itemFulfillmentId);
}
// Saving Item fulfillment to custom record - Maersk Integration Info
record.submitFields({
type: "customrecord_jj_cr_maersk_integrtn_info",
id: customRecordId.toString(),
values: {
"custrecord_jj_item_fulfillment_micl300": itemFulfillmentArray,
"custrecord_jj_error_if_micl300": "",
"custrecord_jj_maersk_pack_json_micl300": JSON.stringify(jsonRequest)
},
options: {
enablesourcing: false,
ignoreMandatoryFields: true
}
});
// Setting the Maersk IF Sync checkbox as checked in IF record
record.submitFields({
type: record.Type.ITEM_FULFILLMENT,
id: returnObj.itemFulfillmentId,
values: {
"custbody_jj_maersk_if_sync_micl300": true,
},
options : {
enableSourceing : false,
ignoreMandatoryFields : true
}
});
}
}
return returnObj;
}
catch (e) {
log.error("Error@createItemFulfillment", e);
returnObj.status = "FAILUE";
returnObj.message = "RECORD_NOT_CREATED";
PROCESS.storeError(customRecordId, e.message);
return responseObj;
}
},
/**
* Function to store the error in the custom record - Maersk Integration Info
* @param {*} maerskIntegInfoId
* @param {*} error
*/
storeError(maerskIntegInfoId, error) {
try {
record.submitFields({
type: "customrecord_jj_cr_maersk_integrtn_info",
id: maerskIntegInfoId.toString(),
values: { "custrecord_jj_err_on_ir_creatn_micl_300": error }
});
}
catch (e) {
log.error("Error@storeError", e.name);
return false;
}
}
}
// Main function where script execution starts from here
let main = {
/**
* Defines the function that is executed when a POST request is sent to a RESTlet.
* @param {string | Object} requestBody - The HTTP request body; request body is passed as a string when request
* Content-Type is 'text/plain' or parsed into an Object when request Content-Type is 'application/json' (in which case
* the body must be a valid JSON)
* @returns {string | Object} HTTP response body; returns a string when request Content-Type is 'text/plain'; returns an
* Object when request Content-Type is 'application/json' or 'application/xml'
* @since 2015.2
*/
post: function (requestBody) {
let responseObj = {};
try {
log.error("Pack Request from Maersk", requestBody);
responseObj = PROCESS.validatePackRequest(requestBody);
if (!maerskUtility.checkForParameter(responseObj.message)) {
responseObj = PROCESS.appendPackItemsInSameLineId(requestBody);
responseObj = PROCESS.processPackData(responseObj);
}
return PROCESS.jsonResponse(responseObj);
}
catch (e) {
log.error("Error@post", e.name);
responseObj.status = "ERROR"
responseObj.message = "RECORD_NOT_CREATED"
return PROCESS.jsonResponse(responseObj);
}
},
/**
* Defines the function that is executed when a POST request is sent to a RESTlet.
* @param {string | Object} requestBody - The HTTP request body; request body is passed as a string when request
* Content-Type is 'text/plain' or parsed into an Object when request Content-Type is 'application/json' (in which case
* the body must be a valid JSON)
* @returns {string | Object} HTTP response body; returns a string when request Content-Type is 'text/plain'; returns an
* Object when request Content-Type is 'application/json' or 'application/xml'
* @since 2015.2
*/
put: function (requestBody) {
let responseObj = {};
try {
log.error("Ship Request from Maersk", requestBody);
responseObj = PROCESS.validateShipRequest(requestBody);
if (!maerskUtility.checkForParameter(responseObj.message)) {
responseObj = PROCESS.processShipData(requestBody);
}
return PROCESS.jsonResponse(responseObj);
}
catch (e) {
log.error("Error@put", e.name);
responseObj.status = "ERROR"
responseObj.message = "RECORD_NOT_UPDATED"
return PROCESS.jsonResponse(responseObj);
}
}
}
return main;
});