NetSuite定时脚本创建CustomerPayment后关联发票不符问题求助
问题描述
通过NetSuite定时脚本循环创建customerpayment对象并关联指定发票,预期保存后能准确关联目标发票,但实际打开记录发现关联的是其他发票。日志显示应关联发票9236675,但实际关联的是9236677。该问题仅在客户环境复现,本地测试环境运行正常。
初始实现代码
if (paydata != null) { paymentfee = paydata.paymentFee; for (var y = 0; y < paydata.payments.length; y++) { var paymentrec = null; log.debug('entity', paydata.payments[y].customerRef); paymentrec = record.create({ type: "customerpayment", defaultValues: { "entity": paydata.payments[y].customerRef } }); paymentrec.setValue({ fieldId: "subsidiary", value: paydata.subsidiaryId }); // 创建带账户的付款时会自动存入 paymentrec.setValue({ fieldId: "account", value: paydata.account }); // 支持自定义表单功能 if (paydata.payments[y].customform) { paymentrec.setValue({ fieldId: "customform", value: paydata.payments[y].customform }); log.debug('Custom form applied', paydata.payments[y].customform); } paymentrec.setValue({ fieldId: "memo", value: paydata.payments[y].note }); paymentrec.setValue({ fieldId: "payment", value: paydata.payments[y].paymentAmount }); // // 调试发票自动应用逻辑 // paymentrec.setValue({ // fieldId: "autoapply", // value: false // }) paymentrec.setValue({ fieldId: "custbody_peakflo_payref", value: paydata.peakfloPaymentRef }); paymentrec.setValue({ fieldId: "custbody_peakflo_payacct", value: paydata.account }); paymentrec.setValue({ fieldId: "custbody_peakflo_payfee", value: paydata.paymentFee }); paymentrec.setText({ fieldId: "currency", value: paydata.currency }); for (var z = 0; z < paydata.payments[y].invoiceLines.length; z++) { if (paydata.payments[y].invoiceLines[z].NetSuiteInvoiceInternalId != '' && paydata.payments[y].invoiceLines[z].NetSuiteInvoiceInternalId != null) { log.debug('count', paymentrec.getLineCount({ sublistId: "apply" })); for (var al = 0; al < paymentrec.getLineCount({sublistId: "apply"}); al++) { log.debug('comparison data', JSON.stringify( { recordInternalId: paymentrec.getSublistValue({ sublistId: "apply", fieldId: "internalid", line: al }), payloadInvoiceId: paydata.payments[y].invoiceLines[z].NetSuiteInvoiceInternalId, isEqual: paymentrec.getSublistValue({ sublistId: "apply", fieldId: "internalid", line: al }) == paydata.payments[y].invoiceLines[z].NetSuiteInvoiceInternalId })); if (paymentrec.getSublistValue({ sublistId: "apply", fieldId: "internalid", line: al }) == paydata.payments[y].invoiceLines[z].NetSuiteInvoiceInternalId) { paymentrec.setSublistValue({ sublistId: "apply", fieldId: "apply", line: al, value: true }); paymentrec.setSublistValue({ sublistId: "apply", fieldId: "amount", line: al, value: paydata.payments[y].invoiceLines[z].PaymentAmount }); var inv_qm = record.create({type: "customrecord_peakflo_history_qm"}); inv_qm.setValue({fieldId: "custrecord_pf_hqm_dg_pri", value: "2"}); inv_qm.setValue({ fieldId: "custrecord_pf_hqm_recsub", value: paymentrec.getValue({fieldId: "subsidiary"}) }); inv_qm.setValue({ fieldId: "custrecord_pf_hqm_rectype", value: "transaction" }); inv_qm.setValue({ fieldId: "custrecord_pf_hqm_recid", value: paydata.payments[y].invoiceLines[z].NetSuiteInvoiceInternalId }); var inv_qmid = inv_qm.save(); log.debug('payment amt', paydata.payments[y].invoiceLines[z].PaymentAmount); log.debug('HQM created id', inv_qmid); } log.debug('Selected', paymentrec.getSublistValue({ sublistId: "apply", fieldId: "apply", line: al })); } } } var paymentrecid = paymentrec.save({ ignoreMandatoryFields: true, enableSourcing: true }); record.submitFields({ type:"customrecord_peakflo_qm", id:pfp_s_res[x].id, values:{ custrecord_pf_processed:true } }); log.debug('payment id', paymentrecid); if (paymentrecid) { paymentref.push(paymentrecid); } } }
8月6日更新后的代码
var paymentrec = record.create({ type: "customerpayment", defaultValues: { "entity": paymentData.customerRef } }); paymentrec.setValue({ fieldId: "subsidiary", value: paydata.subsidiaryId }); // 创建带账户的付款时会自动存入 paymentrec.setValue({ fieldId: "account", value: paydata.account }); // 支持自定义表单功能 if (paymentData.customform) { paymentrec.setValue({ fieldId: "customform", value: paymentData.customform }); log.debug('Custom form applied', paymentData.customform); } paymentrec.setValue({ fieldId: "memo", value: paymentData.note }); paymentrec.setValue({ fieldId: "payment", value: paymentData.paymentAmount }); paymentrec.setValue({ fieldId: "custbody_peakflo_payref", value: paydata.peakfloPaymentRef }); paymentrec.setValue({ fieldId: "custbody_peakflo_payacct", value: paydata.account }); paymentrec.setValue({ fieldId: "custbody_peakflo_payfee", value: paydata.paymentFee }); paymentrec.setText({ fieldId: "currency", value: paydata.currency }); var anyApplied = false; // 遍历paymentrec中所有未结发票行 peakflo.iter(paymentrec, 'apply', function(al, getV, setV) { // 将付款应用行作为外层循环,可关闭未匹配行的应用状态 var didFind = false; // 遍历所有输入的付款数据发票行 paymentData.invoiceLines.forEach(function(invLine, z) { if (!invLine.NetSuiteInvoiceInternalId || getV('doc') != invLine.NetSuiteInvoiceInternalId) return; didFind = true; anyApplied = true; setV('apply', true); setV('amount', invLine.PaymentAmount); log.debug('payment amt', JSON.stringify( { payloadAmount: invLine.PaymentAmount, recordLineAmount: paymentrec.getSublistValue({ sublistId: "apply", fieldId: "amount", line: al }) } ) ); log.debug('Selected', paymentrec.getSublistValue({ sublistId: "apply", fieldId: "apply", line: al })); }); // 如果在 payload 中未找到对应发票,设置为不应用 if (!didFind) setV('apply', false); }); // 如果有任何发票被应用 if (anyApplied) { // 保存付款记录 var paymentrecid = paymentrec.save({ ignoreMandatoryFields: true, enableSourcing: false // 默认值为false,设为true可能引发异常行为。已初始化客户默认值,无需进一步自动填充 }); record.submitFields({ type: "customrecord_peakflo_qm", id: pfp_s_res[x].id, values: { custrecord_pf_processed: true } }); log.debug('payment id', paymentrecid); if (paymentrecid) { paymentref.push(paymentrecid); } } else { log.error({ title: 'no open invoice found', details: JSON.stringify(pd) }); } });
可能的原因及排查方向
1. 自动应用逻辑的环境差异
客户环境可能开启了全局Auto Apply Payments设置,或使用的自定义表单默认启用自动应用。即使代码手动设置了发票关联,NetSuite保存时可能触发自动应用逻辑,覆盖手动设置。建议强制关闭自动应用:
paymentrec.setValue({ fieldId: "autoapply", value: false });
创建customerpayment时就设置该字段,避免保存时自动重新匹配发票。
2. 字段类型不匹配导致的比较错误
检查NetSuiteInvoiceInternalId的数据类型:如果该值是字符串,而getSublistValue('doc')返回数值,使用==比较可能因隐式转换出现匹配错误。建议改用严格相等===确保类型一致:
if (!invLine.NetSuiteInvoiceInternalId || getV('doc') !== invLine.NetSuiteInvoiceInternalId) return;
3. 多子公司/多货币环境的加载顺序问题
客户环境可能是多子公司、多货币的复杂环境,创建customerpayment时的字段设置顺序(如子公司、实体、货币)可能影响发票列表加载顺序,导致循环匹配错位。建议调整顺序:先设置子公司,再设置实体,最后设置货币及其他字段,确保发票列表加载正确。
4. 第三方脚本/工作流的干扰
客户环境中可能存在其他自定义脚本(如用户事件脚本)或工作流,在customerpayment保存的beforeSubmit或afterSubmit阶段触发,修改了apply子列表的关联数据。需要排查客户环境中针对customerpayment的自定义脚本和工作流,确认是否有逻辑干扰发票关联。
5. 内存中记录的缓存问题
创建customerpayment后,apply子列表的发票数据可能未完全加载,导致代码读取的发票ID或排序与实际保存时不一致。可尝试在设置完实体和子公司后,调用paymentrec.reload()重新加载记录(仅适用于部分NetSuite版本),或改用record.load方式创建支付记录,确保发票列表加载完整。
内容的提问来源于stack exchange,提问作者Dmitry Vedenyapin

