You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SuiteScript 2.0 MapReduce脚本无法创建交易问题排查与修复

NetSuite MapReduce脚本Reduce阶段异常问题及修复

问题描述

我编写的NetSuite MapReduce脚本在进入Reduce阶段前运行正常,但Reduce阶段出现异常。日志调试执行到以下代码后停止,无任何日志输出也无错误提示:

var salesOrderId = salesOrderRec.save();

if (salesOrderId) {
  log.debug("Sales Order ID", salesOrderId);
} else {
  log.debug("Failed to create Sales Order");
}
}
var salesOrderId = salesOrderRec.save();
log.debug("Sales Order ID", salesOrderId);

我尝试过调整代码(比如将销售订单保存操作移至循环内),但仍无法解决问题,求指出代码中的问题。

原问题脚本

/**
 *@NApiVersion 2.x
 *@NScriptType MapReduceScript
 */
define(["N/search", "N/record"], function (search, record) {
  function getInputData() {
    var salesorderSearchObj = search.create({
      type: "salesorder",
      filters: [
        ["type", "anyof", "SalesOrd"],
        "AND",
        ["mainline", "is", "F"],
        "AND",
        ["taxline", "is", "F"],
        "AND",
        ["shipping", "is", "F"],
        "AND",
        ["item", "noneof", "@NONE@"],
        "AND",
        ["status", "anyof", "SalesOrd:F"],
      ],
      columns: [
        search.createColumn({ name: "tranid", label: "Document Number" }),
        search.createColumn({ name: "entity", label: "Name" }),
        search.createColumn({ name: "item", label: "Item" }),
        search.createColumn({ name: "rate", label: "Item Rate" }),
        search.createColumn({ name: "quantity", label: "Quantity" }),
        search.createColumn({ name: "amount", label: "Amount" }),
        search.createColumn({ name: "taxamount", label: "Amount (Tax)" }),
        search.createColumn({ name: "grossamount", label: "Amount (Gross)" }),
        search.createColumn({ name: "taxcode", label: "Tax Item" }),
        search.createColumn({ name: "memo", label: "Memo" }),
        search.createColumn({
          name: "lineuniquekey",
          label: "Line Unique Key",
        }),
      ],
    });
    return salesorderSearchObj;
  }
  /**
   * @param {MapReduceContext.map} context
   */
  function map(context) {
    var result = JSON.parse(context.value);
    log.debug("result", result);
    const entityValue = result.values.entity.value;
    const itemValue = result.values.item.value;
    const quantityValue = result.values.quantity;
    const rateValue = result.values.rate;
    const taxValue = result.values.taxamount;
    const amountValue = result.values.amount;
    const taxcodeValue = result.values.taxcode.value;

    // 存储商品属性的对象
    const itemObj = {
      item: itemValue,
      quantity: quantityValue,
      rate: rateValue,
      amount: amountValue,
      taxamount: taxValue,
      taxcode: taxcodeValue,
    };

    // 按客户分组商品
    const entityItems = {};
    if (entityValue in entityItems) {
      entityItems[entityValue].push(itemObj);
    } else {
      entityItems[entityValue] = [itemObj];
    }

    context.write({
      key: entityValue,
      value: JSON.stringify(entityItems),
    });
    log.debug("map context debug", context);
  }
  /**
   * @param {MapReduceContext.reduce} context
   */
  function reduce(context) {
    log.debug("reduce context", context);

    var values = context.values;
    log.debug("context.values", values);
    var entity = context.key;
    log.debug("context.key", entity);

    log.debug("values.length", values.length)
    for (var i = 0; i < values.length; i++) {
      var jsonStr = values[i];
      log.debug("jsonstr", jsonStr);
      var entityData = JSON.parse(jsonStr);
      var salesOrderRec = record.create({
        type: record.Type.SALES_ORDER,
        isDynamic: true,
      });

      log.debug({
        title: "salesOrderRec",
        details: salesOrderRec,
      });
      for (var entity in entityData) {
        if (entityData.hasOwnProperty(entity)) {
          var items = entityData[entity];
          for (var j = 0; j < items.length; j++) {
            var itemData = items[j];

            // 提取商品属性
            var item = itemData.item;
            var quantity = itemData.quantity;
            var rate = itemData.rate;
            var amount = itemData.amount;
            var taxamount = itemData.taxamount;
            var taxcode = itemData.taxcode;

            log.debug("item:", item);
            log.debug("quantity:", quantity);
            log.debug("rate:", rate);
            log.debug("amount:", amount);
            log.debug("taxamt:", taxamount);

            salesOrderRec.setValue({
              fieldId: "entity",
              value: entity, // 设置客户ID
            });

            salesOrder.selectNewLine({
              sublistId: "item",
            });

            salesOrder.setCurrentSublistValue({
              sublistId: "item",
              fieldId: "item",
              value: item,
            });

            salesOrder.setCurrentSublistValue({
              sublistId: "item",
              fieldId: "quantity",
              value: quantity,
            });

            // 此处可补充设置其他行字段

            salesOrderRec.commitLine({
              sublistId: "item",
            });
          }
        }
      }

      // 保存销售订单
      var salesOrderId = salesOrderRec.save();

      if (salesOrderId) {
        log.debug("Sales Order ID", salesOrderId);
      } else {
        log.debug("Failed to create Sales Order");
      }
    }
    var salesOrderId = salesOrderRec.save();
    log.debug("Sales Order ID", salesOrderId);
  }

  /**
   * @param {MapReduceContext.summarize} context
   */
  function summarize(summary) {}
  return {
    getInputData: getInputData,
    map: map,
    reduce: reduce,
    summarize: summarize,
  };
});

问题关键原因

  1. 变量名不统一:创建的记录对象为salesOrderRec,但操作子列表时错误使用了未定义的salesOrder变量,触发静默异常导致脚本终止。
  2. 重复创建订单:在values循环内每次迭代都新建销售订单,同一客户的每个商品条目会生成独立订单,不符合分组需求。
  3. 重复保存操作:循环结束后再次调用save(),此时salesOrderRec仅指向最后一次创建的对象,且重复保存无意义。
  4. 缺少异常捕获:未对记录操作添加异常捕获逻辑,无法定位错误原因。

可正常运行的修复版本

/**
 * @NApiVersion 2.x
 * @NScriptType MapReduceScript
 */
define(["N/search", "N/record"], function (search, record) {
  function getInputData() {
    var invoiceSearchObj = search.create({
      type: "invoice",
      filters: [
        ["type", "anyof", "CustInvc"],
        "AND",
        ["mainline", "is", "F"],
        "AND",
        ["taxline", "is", "F"],
        "AND",
        ["item", "noneof", "@NONE@"],
        "AND",
        ["item.revenuerecognitionrule", "anyof", "@NONE@"],
        "AND",
        ["currency", "anyof", "5"],
        "AND",
        ["datecreated", "within", "thisfiscalquarter"],
      ],
      columns: [
        search.createColumn({
          name: "internalid",
          join: "customerMain",
          label: "客户(主行):内部ID",
        }),
        search.createColumn({ name: "entity", label: "名称" }),
        search.createColumn({ name: "tranid", label: "单据编号" }),
        search.createColumn({ name: "item", label: "商品" }),
        search.createColumn({ name: "trandate", label: "日期" }),
        search.createColumn({ name: "amount", label: "金额" }),
        search.createColumn({ name: "taxamount", label: "税额" }),
        search.createColumn({ name: "rate", label: "商品单价" }),
        search.createColumn({ name: "quantity", label: "数量" }),
        search.createColumn({ name: "pricelevel", label: "价格等级" }),
        search.createColumn({ name: "taxcode", label: "税项" }),
      ],
    });
    return invoiceSearchObj;
  }

  function map(context) {
    var result = JSON.parse(context.value);
    log.debug("result", result);

    const entityValue = result.values.entity.value;
    const itemValue = result.values.item.value;
    const quantityValue = result.values.quantity;
    const rateValue = result.values.rate;
    const taxValue = result.values.taxamount;
    const amountValue = result.values.amount;
    const taxcodeValue = result.values.taxcode.value;

    const itemObj = {
      item: itemValue,
      quantity: quantityValue,
      rate: rateValue,
      amount: amountValue,
      taxamount: taxValue,
      taxcode: taxcodeValue,
    };

    const entityItems = {};

    if (entityValue in entityItems) {
      entityItems[entityValue].push(itemObj);
    } else {
      entityItems[entityValue] = [itemObj];
    }

    context.write({
      key: entityValue,
      value: JSON.stringify(entityItems),
    });
    log.debug("map context debug", context);
  }

function reduce(context) {
  log.debug("reduce context", context);

  var values = context.values;
  log.debug("context.values", values);
  var entity = context.key;
  log.debug("context.key", entity);

  var salesOrderRec; // 在循环外声明订单对象

  for (var i = 0; i < values.length; i++) {
    var jsonStr = values[i];
    log.debug("jsonstr", jsonStr);
    var entityData = JSON.parse(jsonStr);

    for (var entity in entityData) {
      if (entityData.hasOwnProperty(entity)) {
        var items = entityData[entity];
        for (var j = 0; j < items.length; j++) {
          var itemData = items[j];

          // 提取商品属性
          var item = itemData.item;
          var quantity = itemData.quantity;
          var rate = itemData.rate;
          var amount = itemData.amount;
          var taxamount = itemData.taxamount;
          var taxcode = itemData.taxcode;

          log.debug("item:", item);
          log.debug("quantity:", quantity);
          log.debug("rate:", rate);
          log.debug("amount:", amount);
          log.debug("taxamt:", taxamount);

          try {
            // 仅当订单未创建时初始化
            if (!salesOrderRec) {
              salesOrderRec = record.create({
                type: record.Type.SALES_ORDER,
                isDynamic: true,
              });
              salesOrderRec.setValue({
                fieldId: "entity",
                value: entity, // 设置客户ID
              });
              salesOrderRec.setValue({
                fieldId: "memo",
                value: JSON.stringify(item),
              });
            }

            salesOrderRec.selectNewLine({
              sublistId: "item",
            });

            salesOrderRec.setCurrentSublistValue({
              sublistId: "item",
              fieldId: "item",
              value: item,
            });

            salesOrderRec.setCurrentSublistValue({
              sublistId: "item",
              fieldId: "quantity",
              value: quantity,
            });

            // 此处可补充设置其他行字段

            salesOrderRec.commitLine({
              sublistId: "item",
            });
          } catch (e) {
            log.error({
              title: "创建销售订单出错: ",
              details: e.message,
            });
          }
        }
      }
    }
  }

  // 所有条目处理完成后统一保存订单
  if (salesOrderRec) {
    var salesOrderId = salesOrderRec.save();
    if (salesOrderId) {
      log.debug("销售订单ID", salesOrderId);
    } else {
      log.debug("创建销售订单失败");
    }
  }
}

  return {
    getInputData: getInputData,
    map: map,
    reduce: reduce,
  };
});

修复说明

  1. 统一变量名:将所有操作记录的变量统一为salesOrderRec,避免未定义变量错误。
  2. 延迟创建订单:仅在首次处理客户条目时创建销售订单,同一客户的所有商品都添加到同一订单的子列表中。
  3. 移除重复保存:仅在所有商品条目处理完成后,执行一次订单保存操作。
  4. 添加异常捕获:对记录操作包裹try-catch块,捕获并输出错误信息,便于问题排查。

内容的提问来源于stack exchange,提问作者Vernita

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 04:32:35