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

NetSuite Suitelet报错:无法向Item Receipt添加未入库PO行项目

问题描述
  • 需求:将采购单(Purchase Order)中未入库的项目重新填充到已有的入库单(Item Receipt)中
  • 场景:采购单包含3个独立行项目,用户仅入库1个并生成入库单,保存后无法在该入库单中添加剩余2个项目,仅能调整已入库数量或新建入库单
  • 问题:通过DOM操作在编辑视图添加按钮触发Suitelet脚本后,添加行时出现报错:

    "You have attempted an invalid sublist or line item operation. You are either trying to access a field on a non-existent line or you are trying to add or remove lines from a static sublist."

  • 身份:NetSuite SuiteScript新手,寻求脚本修正及逻辑优化建议
当前脚本代码
/**
 * @NApiVersion 2.x
 * @NScriptType Suitelet
 */
define(['N/record', 'N/log'], function (record, log) {
    function onRequest(context) {
        log.debug('Suitelet Entry', 'The onRequest function has been triggered.');

        try {
            // Gets request parameters
            var params = context.request.parameters;
            log.debug('Request Parameters', params);

            var itemReceiptId = params.recordId;

            if (!itemReceiptId) {
                log.error('Error', 'No Item Receipt ID provided.');
                return;
            }

            log.debug('Item Receipt ID Received', itemReceiptId);

            // Load the Item Receipt record as dynamic
            var itemReceipt = record.load({
                type: record.Type.ITEM_RECEIPT,
                id: itemReceiptId,
                isDynamic: true // dynamic mode for line manipulation? dont know if this is right
            });

            log.debug('Item Receipt Loaded', 'Item Receipt ID: ' + itemReceiptId);

            // Get linked Purchase Order ID from Item Receipt
            var poId = itemReceipt.getValue('createdfrom');
            if (!poId) {
                log.error('Error', 'The Item Receipt is not linked to any Purchase Order.');
                return;
            }

            log.debug('Linked PO ID Retrieved', poId);

            // Load the Purchase Order record
            var poRecord = record.load({
                type: record.Type.PURCHASE_ORDER,
                id: poId
            });

            var poLineCount = poRecord.getLineCount({ sublistId: 'item' });
            var irLineCount = itemReceipt.getLineCount({ sublistId: 'item' });

            log.debug('Line Counts Retrieved', 'PO Lines: ' + poLineCount + ', IR Lines: ' + irLineCount);

            // Track existing items in Item Receipt using  array
            var existingItems = [];
            for (var i = 0; i < irLineCount; i++) {
                itemReceipt.selectLine({ sublistId: 'item', line: i });
                var irItem = itemReceipt.getCurrentSublistValue({
                    sublistId: 'item',
                    fieldId: 'item'
                });
                existingItems.push(irItem);
                log.debug('Existing Item in IR', 'Line ' + (i + 1) + ': ' + irItem);
            }

            log.debug('Existing Items Array', JSON.stringify(existingItems));

            // Compare PO lines and the IR lines to add missing lines
            for (var i = 0; i < poLineCount; i++) {
                var poItem = poRecord.getSublistValue({
                    sublistId: 'item',
                    fieldId: 'item',
                    line: i
                });

                // Check if the PO item exists in the Item Receipt using a manual loop
                var itemExists = false;
                for (var j = 0; j < existingItems.length; j++) {
                    if (existingItems[j] == poItem) { // Useing strict equality to compare
                        itemExists = true;
                        break;
                    }
                }

                if (!itemExists) {
                    log.debug('Missing Line Found', 'Adding PO Item ID: ' + poItem);

                    // Add new line to the dynamic sublist
                    try {
                        itemReceipt.selectNewLine({ sublistId: 'item' });
                        itemReceipt.setCurrentSublistValue({
                            sublistId: 'item',
                            fieldId: 'item',
                            value: poItem
                        });
                        itemReceipt.setCurrentSublistValue({
                            sublistId: 'item',
                            fieldId: 'quantity',
                            value: poRecord.getSublistValue({
                                sublistId: 'item',
                                fieldId: 'quantity',
                                line: i
                            })
                        });
                        itemReceipt.commitLine({ sublistId: 'item' });

                        log.debug('Missing Line Added', 'PO Item ID ' + poItem + ' added to Item Receipt.');
                    } catch (e) {
                        log.error('Error Adding Line', 'Failed to add PO Item ID ' + poItem + ': ' + e.message);
                    }
                }
            }

            // Save updated Item Receipt record
            try {
                var updatedRecordId = itemReceipt.save();
                log.debug('Resync Completed', 'Item Receipt ' + updatedRecordId + ' updated successfully.');
            } catch (e) {
                log.error('Error Saving Item Receipt', e.message);
            }

            // Redirect back 2 Item Receipt record in Edit mode
            context.response.sendRedirect({
                type: context.response.RedirectType.RECORD,
                identifier: record.Type.ITEM_RECEIPT,
                id: itemReceiptId,
                isEditMode: true
            });
        } catch (e) {
            log.error('Error During Resync Process', e.message);
        }
    }

    return {
        onRequest: onRequest
    };
});
修正方案与优化建议

报错原因分析

已保存且关联采购单的Item Receipt,其item子列表属于静态子列表——即使以动态模式加载,也不能直接通过selectNewLine添加独立的item行。必须关联到采购单中未完成入库的具体行,才能合法添加条目。

核心代码修正

1. 替换行存在性判断逻辑

原代码用item ID判断是否存在,无法区分采购单中同一item的多行。改为跟踪Item Receipt中已关联的采购单行ID:

// 替换原有的existingItems数组为Set,存储已关联的PO行ID
var existingPOLineIds = new Set();
for (var i = 0; i < irLineCount; i++) {
    itemReceipt.selectLine({ sublistId: 'item', line: i });
    var poLineId = itemReceipt.getCurrentSublistValue({
        sublistId: 'item',
        fieldId: 'purchaseorderline'
    });
    if (poLineId) {
        existingPOLineIds.add(poLineId);
        log.debug('Existing PO Line in IR', 'Line ' + (i + 1) + ': PO Line ID ' + poLineId);
    }
}

2. 修改新增行逻辑,关联采购单行

新增行时必须先绑定采购单行,让NetSuite自动填充item及其他字段,而不是直接设置item:

// 遍历PO行,处理未入库的条目
for (var i = 0; i < poLineCount; i++) {
    var poLineId = poRecord.getSublistValue({
        sublistId: 'item',
        fieldId: 'lineuniquekey',
        line: i
    });
    var poQty = poRecord.getSublistValue({ sublistId: 'item', fieldId: 'quantity', line: i });
    var receivedQty = poRecord.getSublistValue({ sublistId: 'item', fieldId: 'receiptquantity', line: i });
    var remainingQty = poQty - receivedQty;

    // 跳过已完全入库或已存在于当前IR的PO行
    if (remainingQty <= 0 || existingPOLineIds.has(poLineId)) {
        continue;
    }

    log.debug('Missing PO Line Found', 'Adding PO Line ID: ' + poLineId + ', Remaining Qty: ' + remainingQty);

    try {
        itemReceipt.selectNewLine({ sublistId: 'item' });
        // 先关联采购单和对应行,NetSuite会自动填充item、单价等字段
        itemReceipt.setCurrentSublistValue({
            sublistId: 'item',
            fieldId: 'purchaseorder',
            value: poId
        });
        itemReceipt.setCurrentSublistValue({
            sublistId: 'item',
            fieldId: 'purchaseorderline',
            value: i // PO行号(注意是从0开始的索引)
        });
        // 设置剩余入库数量
        itemReceipt.setCurrentSublistValue({
            sublistId: 'item',
            fieldId: 'quantity',
            value: remainingQty
        });
        itemReceipt.commitLine({ sublistId: 'item' });

        log.debug('PO Line Added', 'PO Line ID ' + poLineId + ' added to Item Receipt with qty ' + remainingQty);
    } catch (e) {
        log.error('Error Adding PO Line', 'Failed to add PO Line ID ' + poLineId + ': ' + e.message);
    }
}

其他优化建议

  • 静态模式加载采购单:采购单仅用于读取数据,用静态模式加载更高效:
    var poRecord = record.load({
        type: record.Type.PURCHASE_ORDER,
        id: poId,
        isDynamic: false
    });
    
  • 使用Set提升查找效率:原代码用数组循环判断存在性,时间复杂度O(n²),改用Set后为O(1),适合多行场景。
  • 增加权限校验:添加N/runtime模块,校验当前用户是否有编辑Item Receipt的权限,避免无权限操作报错。
  • 处理小数精度:NetSuite中数量可能为小数,计算剩余数量时建议用parseFloat处理,避免精度丢失。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 06:30:02