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

【求助】NetSuite SuiteScript按钮无法关闭Sales Order问题

SuiteScript关闭Sales Order失败问题排查

作为SuiteScript新手,目前遇到通过按钮关闭Sales Order失败的问题:已为状态为**待审批(pending approval)或待发货(pending fulfillments)**的Sales Order添加用户事件按钮,点击按钮后Suitelet可捕获到Sales Order ID,但无法完成订单关闭操作。以下是相关脚本代码,请求协助排查解决:

Client Script

/**
 * @NApiVersion 2.1
 * @NScriptType ClientScript
 */
define(['N/url', 'N/ui/dialog', 'N/log', 'N/currentRecord'], (url, dialog, log, currentRecord) => {
    function pageInit(scriptContext) {

    }

    function showPopup() {
        try {
            const currentRec = currentRecord.get();
            const salesOrderId = currentRec.id;
            const paramsId = {
                orderId : salesOrderId
            }

            const suiteletUrl = url.resolveScript({
                scriptId: 'customscript_bits_ss_close_sales_order',
                deploymentId: 'customdeploy_bits_ss_close_sales_order',
                params: paramsId
            });
            dialog.create({
                title: 'Cancel/Close Order',
                message: `<iframe src="${suiteletUrl}" style="width: 100%; height: 200px; border: none;"></iframe>`,
                buttons: [
                    {
                        label: 'Cancel',
                        value: 'close',
                        action: () => dialog.close()
                    }
                ]
            });

        } catch (error) {
            log.error('Error in showPopup', error.message);
            alert('An error occurred while opening the popup. Check the logs for details.');
        }
    }

    return {
        pageInit,
        showPopup
    };
});

Suitelet Script

/**
 * @NApiVersion 2.1
 * @NScriptType Suitelet
 */
define(['N/record', 'N/ui/serverWidget', 'N/log'], (record, serverWidget, log) => {
    const onRequest = (scriptContext) => {
        const request = scriptContext.request;
        const response = scriptContext.response;

        const salesOrderId = request.parameters['orderId'] ;
        log.debug('Sales Order ID Received', salesOrderId);

        if (!salesOrderId) {
            response.write('<p>Error: Sales Order ID is missing.</p>');
            return;
        }

        if (request.method === 'GET') {
            try {
                const salesOrder = record.load({
                    type: record.Type.SALES_ORDER,
                    id: salesOrderId
                });
                log.debug('Sales Order Loaded Successfully', salesOrder);

                const form = serverWidget.createForm({
                    title: `Close Sales Order - ID: ${salesOrderId}`,
                    hideNavBar: true
                });

                form.addField({
                    id: 'custpage_message',
                    type: serverWidget.FieldType.INLINEHTML,
                    label: 'Message'
                }).defaultValue = `<p>Are you sure you want to cancel Sales Order ID: ${salesOrderId}?</p>`;

                form.addSubmitButton({ label: 'Confirm Cancellation' });

                form.addButton({
                    id: 'custpage_cancel',
                    label: 'Cancel',
                    functionName: 'window.close();'
                });

                response.writePage(form);
            } catch (error) {
                log.error('Error Loading Sales Order', error);
                response.write('<p>Error: Unable to load the Sales Order. Check the ID and try again.</p>');
            }
        } else if (request.method === 'POST') {
            try {
                const salesOrder = record.load({
                    type: record.Type.SALES_ORDER,
                    id: salesOrderId
                });

                const lineCount = salesOrder.getLineCount({ sublistId: 'item' });
                for (let i = 0; i < lineCount; i++) {
                    salesOrder.setSublistValue({
                        sublistId: 'item',
                        fieldId: 'isclosed',
                        line: i,
                        value: true
                    });
                }

                salesOrder.save();
                response.write('<script>alert("Sales Order canceled successfully."); window.close();</script>');
            } catch (error) {
                log.error('Error Cancelling Sales Order', error);
                response.write('<script>alert("Failed to cancel the Sales Order."); window.close();</script>');
            }
        }
    };

    return { onRequest };
});

排查与解决要点

  • 检查订单状态与工作流锁定:待审批的订单可能处于工作流审批环节,此时订单被系统锁定无法编辑。需先确认订单是否在工作流中,必要时暂停或完成审批后再操作;同时确保脚本部署的角色拥有编辑Sales Order及关闭订单行的权限。
  • 修正订单关闭的逻辑:NetSuite中关闭/取消Sales Order的标准方式不是仅设置行项目的isclosed字段,建议直接使用record.cancel()API,或者同时设置订单头部的status字段(如Cancelled):
    // 使用cancel API取消订单
    record.cancel({
        type: record.Type.SALES_ORDER,
        id: salesOrderId
    });
    
  • 传递POST请求的orderId参数:当前Suitelet的POST请求中,orderId参数会丢失,因为表单提交时没有传递该值。需在GET方法创建表单时添加隐藏字段存储订单ID:
    // 在GET的form创建代码中添加
    form.addField({
        id: 'custpage_orderid',
        type: serverWidget.FieldType.TEXT,
        label: 'Order ID'
    }).updateDisplayType({displayType: serverWidget.FieldDisplayType.HIDDEN}).defaultValue = salesOrderId;
    
    然后在POST中改为const salesOrderId = request.parameters['custpage_orderid'];获取参数。
  • 查看详细错误日志:登录NetSuite后台,进入Setup > Audit Trail > Script Execution Logs,找到Suitelet对应的POST请求日志,查看具体错误信息(如权限不足、字段不可编辑等),这是最直接的排查依据。
  • 优化save方法参数:调用salesOrder.save()时,可添加参数避免必填字段或 sourcing 问题导致保存失败:
    salesOrder.save({
        enableSourcing: false,
        ignoreMandatoryFields: true
    });
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 06:24:54