SuiteScript 将 Invoice 转为 Customer Payment 时报错
结论
Invoice 可以 transform 为 Customer Payment。这次失败并非因为不支持 transform,而是生成的 Customer Payment 缺少必填字段 Project 和 Department,因此无法保存。
在 record.transform() 返回付款记录后、调用 save() 前,使用 setValue() 填写这两个字段:
var paymentRecord = record.transform({
fromType: record.Type.INVOICE,
fromId: invoiceId,
toType: record.Type.CUSTOMER_PAYMENT,
isDynamic: true
});
paymentRecord.setValue({
fieldId: "department",
value: departmentId
});
paymentRecord.setValue({
fieldId: "custbody_project", // 替换为账户中 Project 字段的真实 ID
value: projectId
});
var paymentId = paymentRecord.save();
错误信息显示的是字段标签,不一定是 SuiteScript 使用的字段 ID。Department 的标准字段 ID 通常是 department。Project 则很可能是自定义正文域、自定义 segment,或者被工作流或脚本设为必填,因此需要先确认它的真实字段 ID。
为什么会在 save() 时报错
record.transform() 只会创建一条尚未保存的 Customer Payment 记录。NetSuite 通常会将客户、币种、应收账款科目和来源 Invoice 等信息带入付款记录,但账户中的自定义字段未必会随之继承。
执行下面这行代码时:
var rid3 = objRecord3.save();
NetSuite 才会进行完整的必填字段验证,检查内容包括:
- Customer Payment 表单上的必填设置;
- Accounting Preferences 中的部门分类要求;
- 自定义字段的必填设置;
- Workflow 设置的必填规则;
- Client Script、User Event 等脚本中的验证逻辑。
所以,这些字段需要在 transform 完成后、save 之前填写。
从 Invoice 复制字段
如果 Invoice 上已经有 Department 和 Project,可以先读取 Invoice,再将字段值复制到 Customer Payment。
define(["N/record"], function(record) {
function afterSubmit(scriptContext) {
var salesOrderId = scriptContext.newRecord.id;
var fulfillmentRecord = record.transform({
fromType: record.Type.SALES_ORDER,
fromId: salesOrderId,
toType: record.Type.ITEM_FULFILLMENT,
isDynamic: true
});
fulfillmentRecord.save();
var invoiceRecord = record.transform({
fromType: record.Type.SALES_ORDER,
fromId: salesOrderId,
toType: record.Type.INVOICE,
isDynamic: true
});
var invoiceId = invoiceRecord.save();
var sourceInvoice = record.load({
type: record.Type.INVOICE,
id: invoiceId,
isDynamic: false
});
var departmentId = sourceInvoice.getValue({
fieldId: "department"
});
// 示例字段 ID,必须替换为账户中 Project 字段的真实 ID
var projectId = sourceInvoice.getValue({
fieldId: "custbody_project"
});
var paymentRecord = record.transform({
fromType: record.Type.INVOICE,
fromId: invoiceId,
toType: record.Type.CUSTOMER_PAYMENT,
isDynamic: true
});
if (departmentId) {
paymentRecord.setValue({
fieldId: "department",
value: departmentId
});
}
if (projectId) {
paymentRecord.setValue({
fieldId: "custbody_project",
value: projectId
});
}
var paymentId = paymentRecord.save();
log.audit({
title: "Customer Payment created",
details: {
invoiceId: invoiceId,
paymentId: paymentId
}
});
}
return {
afterSubmit: afterSubmit
};
});
如果这两个值来自 Sales Order,也可以直接从 scriptContext.newRecord 读取,无须重新加载 Invoice:
var departmentId = scriptContext.newRecord.getValue({
fieldId: "department"
});
var projectId = scriptContext.newRecord.getValue({
fieldId: "custbody_project"
});
这样做的前提是 Sales Order 上确实存在这些字段,而且字段值符合 Customer Payment 的取值范围。
如何确认 Project 的字段 ID
不要把错误信息中的 Project 直接当成字段 ID:
// 通常是错误的
paymentRecord.setValue({
fieldId: "Project",
value: projectId
});
应通过 Customer Payment 表单或自定义字段设置确认:
- 打开一条 Customer Payment。
- 启用 NetSuite 的“显示内部 ID”偏好。
- 查看
Project字段的内部 ID。 - 如果它是自定义正文域,ID 通常类似
custbody_project。 - 如果它是自定义 segment,ID 可能类似
cseg_project,具体取决于账户配置。 - 如果页面上找不到该字段,检查 Workflow、User Event 或当前使用的 Custom Form,确认是否有规则在保存时强制要求填写该字段。
setValue() 的 value 应使用字段值的内部 ID,而不是界面上显示的名称。例如:
paymentRecord.setValue({
fieldId: "department",
value: 12
});
这里的 12 是某个 Department 的内部 ID,而不是字符串 "Sales Department"。
必要时显式应用 Invoice
正常情况下,由 Invoice transform 得到的 Customer Payment 会自动关联来源发票。如果账户配置或脚本导致 apply 状态不正确,可以在保存前检查对应行,并显式勾选:
var lineCount = paymentRecord.getLineCount({
sublistId: "apply"
});
for (var i = 0; i < lineCount; i++) {
paymentRecord.selectLine({
sublistId: "apply",
line: i
});
var appliedInvoiceId = paymentRecord.getCurrentSublistValue({
sublistId: "apply",
fieldId: "internalid"
});
if (String(appliedInvoiceId) === String(invoiceId)) {
paymentRecord.setCurrentSublistValue({
sublistId: "apply",
fieldId: "apply",
value: true
});
paymentRecord.commitLine({
sublistId: "apply"
});
break;
}
}
只有 transform 后没有自动应用发票时,才需要加入这段逻辑。
注意事项
不建议用以下方式绕过错误:
paymentRecord.save({
ignoreMandatoryFields: true
});
这个选项可能会绕过一部分自定义必填检查,但不保证能跳过系统必填字段、工作流或其他脚本的校验,还可能生成分类信息不完整的会计交易。更稳妥的处理方式是确认 Project 和 Department 的真实字段 ID,并在保存付款记录前为它们设置有效的内部 ID。
如果已经通过 setValue() 设置字段,但仍然出现相同错误,需要继续检查 Customer Payment 使用的 Custom Form、Workflow,以及部署在 Customer Payment 上的 User Event。错误中显示的字段名称可能来自这些自定义验证规则。
备注:内容仅供参考。
