Restlet API for Item Fulfillment Creation in NetSuite

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;
  });

Leave a comment

Your email address will not be published. Required fields are marked *