Map Reduce Script for Closing Work Orders

To close the work orders based on the search results, use the following code section. If a work order cannot be closed due to any unexpected errors or issues, the script will automatically send an email to the user, providing them with information about the encountered error.

Consider the email sender: EMAIL_AUTHOR
Email receiver: EMAIL_RECIPIENT

define(["N/email", "N/error", "N/record", "N/search", "N/file"],
/**
 * @param{email} email
 * @param{error} error
 * @param{record} record
 * @param{search} search
 */ (email, error, record, search, file) => {
    const EMAIL_AUTHOR = 01;
    const EMAIL_RECIPIENT = 02;
    /**
     * @description Function to remove comma and new line in a string
     * @param {string} data
     * @returns {string}
     */
    const escapeComma = (data) => {
      try {
        let result = data.toString().replaceAll(/,/g, "");
        result = result.toString().replaceAll(/\n/g, "");
        return result;
      } catch (error) {
        log.error("Error @escapeComma", error);
        return data;
      }
    };
    /**
     * @description Check whether the given parameter argument has value on it or is it empty.
     * ie, To check whether a value exists in parameter
     * @param {*} parameter parameter which contains/references some values
     * @param {*} parameterName name of the parameter, not mandatory
     * @returns {Boolean} true if there exist a value else false
     */
    const checkForParameter = (parameter) => {
      try {
        if (
          parameter !== "" &&
          parameter !== null &&
          parameter !== undefined &&
          parameter !== false &&
          parameter !== "null" &&
          parameter !== "undefined" &&
          parameter !== " " &&
          parameter !== "false"
        ) {
          return true;
        } else {
          return false;
        }
      } catch (e) {
        log.error("error@checkForParameter", e);
        return false;
      }
    };

    /**
     * Defines the function that is executed at the beginning of the map/reduce process and generates the input data.
     * @param {Object} inputContext
     * @param {boolean} inputContext.isRestarted - Indicates whether the current invocation of this function is the first
     *     invocation (if true, the current invocation is not the first invocation and this function has been restarted)
     * @param {Object} inputContext.ObjectRef - Object that references the input data
     * @typedef {Object} ObjectRef
     * @property {string|number} ObjectRef.id - Internal ID of the record instance that contains the input data
     * @property {string} ObjectRef.type - Type of the record instance that contains the input data
     * @returns {Array|Object|Search|ObjectRef|File|Query} The input data to use in the map/reduce process
     * @since 2015.2
     */

    const getInputData = (inputContext) => {
      try {
        return search.create({
          type: "workorder",
          filters: [
            ["type", "anyof", "WorkOrd"],
            "AND",
            ["status", "anyof", "WorkOrd:D"],
            "AND",
            ["cogs", "is", "F"],
            "AND",
            ["mainline", "is", "T"],
            "AND",
            ["shipping", "is", "F"],
            "AND",
            ["formulanumeric: {built}","greaterthan","0"],
          ],
          columns: [
            search.createColumn({
              name: "internalid",
              sort: search.Sort.ASC,
              label: "Internal ID",
            }),
            search.createColumn({ name: "trandate", label: "Date" }),
            search.createColumn({ name: "tranid", label: "Document Number" }),
            search.createColumn({ name: "quantity", label: "Quantity" }),
            search.createColumn({ name: "item", label: "Item" }),
            search.createColumn({ name: "built", label: "Built" }),
          ],
        });
      } catch (e) {
        log.error({ title: "error in getInputData", details: e });
        return [];
      }
    };

    /**
     * Defines the function that is executed when the reduce entry point is triggered. This entry point is triggered
     * automatically when the associated map stage is complete. This function is applied to each group in the provided context.
     * @param {Object} reduceContext - Data collection containing the groups to process in the reduce stage. This parameter is
     *     provided automatically based on the results of the map stage.
     * @param {Iterator} reduceContext.errors - Serialized errors that were thrown during previous attempts to execute the
     *     reduce function on the current group
     * @param {number} reduceContext.executionNo - Number of times the reduce function has been executed on the current group
     * @param {boolean} reduceContext.isRestarted - Indicates whether the current invocation of this function is the first
     *     invocation (if true, the current invocation is not the first invocation and this function has been restarted)
     * @param {string} reduceContext.key - Key to be processed during the reduce stage
     * @param {List<String>} reduceContext.values - All values associated with a unique key that was passed to the reduce stage
     *     for processing
     * @since 2015.2
     */
    const reduce = (reduceContext) => {
      let data = JSON.parse(reduceContext.values[0]);
      let reduceValues = data.values;
      let workorderID = reduceValues?.internalid?.value;
      let date = reduceValues.trandate;
      let documentNumber = reduceValues.tranid;
      let quantity = reduceValues.quantity;
      let item = reduceValues?.item?.text;
      let built = reduceValues.built;
      try {
        let completionRecord = record
          .transform({
            fromType: record.Type.WORK_ORDER,
            fromId: workorderID,
            toType: record.Type.WORK_ORDER_CLOSE,
            isDynamic: true,
          })
          .save();
        if (!checkForParameter(completionRecord)) {
          reduceContext.write({
            key: workorderID,
            value: [date, documentNumber, quantity, item, built, " "],
          });
        }
      } catch (error) {
        log.error("Error @reduceContext", error);
        reduceContext.write({
          key: workorderID,
          value: [date, documentNumber, quantity, item, built, error.message],
        });
      }
    };

    /**
     * Defines the function that is executed when the summarize entry point is triggered. This entry point is triggered
     * automatically when the associated reduce stage is complete. This function is applied to the entire result set.
     * @param {Object} summaryContext - Statistics about the execution of a map/reduce script
     * @param {number} summaryContext.concurrency - Maximum concurrency number when executing parallel tasks for the map/reduce
     *     script
     * @param {Date} summaryContext.dateCreated - The date and time when the map/reduce script began running
     * @param {boolean} summaryContext.isRestarted - Indicates whether the current invocation of this function is the first
     *     invocation (if true, the current invocation is not the first invocation and this function has been restarted)
     * @param {Iterator} summaryContext.output - Serialized keys and values that were saved as output during the reduce stage
     * @param {number} summaryContext.seconds - Total seconds elapsed when running the map/reduce script
     * @param {number} summaryContext.usage - Total number of governance usage units consumed when running the map/reduce
     *     script
     * @param {number} summaryContext.yields - Total number of yields when running the map/reduce script
     * @param {Object} summaryContext.inputSummary - Statistics about the input stage
     * @param {Object} summaryContext.mapSummary - Statistics about the map stage
     * @param {Object} summaryContext.reduceSummary - Statistics about the reduce stage
     * @since 2015.2
     */
    const summarize = (summaryContext) => {
      try {
        let header =
          "Record Id,Date,Document Number,Quantity,Item,Built, Reason\n";
        let content = "";
        summaryContext.output.iterator().each(function (key, value) {
          value = JSON.parse(value);
          content =
            content +
            escapeComma(key) +
            "," +
            escapeComma(value[0]) +
            "," +
            escapeComma(value[1]) +
            "," +
            escapeComma(value[2]) +
            "," +
            escapeComma(value[3]) +
            "," +
            escapeComma(value[4]) +
            "," +
            escapeComma(value[5]) +
            "\n";
          return true;
        });
        if (checkForParameter(content)) {
          let fileObj = file.create({
            name: "Details of unprocessed Work Orders_.csv",
            fileType: file.Type.CSV,
            contents: header + content,
            encoding: file.Encoding.UTF8,
          });
          let subject = "Unprocessed Work Orders";
          checkForParameter(fileObj) &&
            email.send({
              author: EMAIL_AUTHOR,
              recipients: EMAIL_RECIPIENT,
              subject: subject,
              body:
                "Hi " +
                ",<br><br>Details of unprocessed work orders are attached here. <br><br>Thank you",
              attachments: [fileObj],
            });
        }
      } catch (error) {
        log.error("Error @summaryContext", error);
      }
    };

    return { getInputData, reduce, summarize };
  });

Leave a comment

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