NetSuite数据集内部代码转文本及CSV结果邮件调度脚本实现
NetSuite数据集API(SuiteScript 2.1)问题与解决方案
问题背景
我用SuiteScript 2.1的N/dataset API获取现有数据集结果,返回的JSON包含PO状态、账单状态、子产品、子公司等字段,但部分字段返回内部代码而非可读文本:
- PO状态返回“F”或“D”(应为“部分收货”这类友好文本)
- 账单状态返回“A”(应为“未结”)
- 子产品返回20(应为子产品名称)
- 子公司返回2/7(内部ID,应为子公司名称)
由于未来可能新增值,无法用静态映射处理转换。
已尝试操作
- 用
row.values[index]提取字段值 - 寻找
getText()方法,但数据集API不支持该方法 - 检查数据集查询的关联设置,确认未影响值的返回
预期与实际结果
| 字段 | 预期结果 | 实际结果 |
|---|---|---|
| PO状态 | Purchase Order: Partially received | D |
| 账单状态 | Bill:Open | A |
| 子产品 | Product Name | 20 |
| 子公司 | Subsidiary Name | 2 |
核心问题
- 为什么数据集返回内部代码(F/D、A、2/7)而非可读文本?
- 如何动态将这些内部值转换为对应的文本标签?
- 如何通过调度脚本将数据集结果转为CSV并邮件发送?
问题解答
1. 为什么返回内部代码?
数据集API默认返回字段的原始存储值:
- 枚举类字段(如PO状态、账单状态)存储的是枚举代码,数据集直接返回代码而非显示文本
- 关联记录字段(如子产品、子公司)存储的是记录内部ID,数据集返回ID而非记录名称
这是因为数据集的设计定位是数据导出、分析场景,优先保证数据原始性,而非前端展示的友好性。
2. 动态转换内部值为可读文本的方案
根据字段类型分两种处理方式:
枚举类字段(PO状态、账单状态)
用N/record API加载对应记录类型,获取字段的所有选项值,动态建立代码到文本的映射:
// 获取枚举字段的选项映射 function getEnumOptions(recordType, fieldId) { const field = record.getField({ type: recordType, fieldId: fieldId }); const optionMap = {}; field.options.forEach(opt => { optionMap[opt.value] = opt.text; }); return optionMap; } // 示例:获取采购订单状态的映射 const poStatusMap = getEnumOptions(record.Type.PURCHASE_ORDER, 'status'); // 使用时:poStatusMap['D'] 即可拿到对应文本
关联记录字段(子产品、子公司)
用N/search API批量查询记录名称,避免单次加载记录的性能损耗:
// 批量获取关联记录的名称映射 function getRecordNameMap(recordType, ids) { const nameMap = {}; if (!ids || ids.length === 0) return nameMap; // 去重ID,避免重复查询 const uniqueIds = [...new Set(ids)]; const searchObj = search.create({ type: recordType, filters: [['internalid', 'anyof', uniqueIds]], columns: ['internalid', 'name'] }); searchObj.run().each(result => { const id = result.getValue('internalid'); const name = result.getValue('name'); nameMap[id] = name; return true; }); return nameMap; } // 示例:批量获取子公司名称映射 const subsidiaryIds = [2,7]; const subsidiaryNameMap = getRecordNameMap(record.Type.SUBSIDIARY, subsidiaryIds); // 使用时:subsidiaryNameMap['2'] 拿到子公司名称
3. 调度脚本实现CSV邮件发送
整合上述转换逻辑,加上CSV生成和邮件发送步骤:
define(['N/dataset', 'N/record', 'N/search', 'N/file', 'N/email', 'N/runtime'], function(dataset, record, search, file, email, runtime) { function execute(context) { // 1. 加载数据集并获取结果 const myDataset = dataset.load({ id: 'custdataset70' }); const results = myDataset.run(); const iterator = results.iterator(); const rows = []; // 2. 预加载需要的映射表 const poStatusMap = getEnumOptions(record.Type.PURCHASE_ORDER, 'status'); const billingStatusMap = getEnumOptions(record.Type.PURCHASE_ORDER, 'billingstatus'); // 先收集所有需要查询的关联ID(请根据实际字段顺序调整索引) const productIds = []; const subsidiaryIds = []; // 第一遍遍历收集ID iterator.each(function(result) { const row = result.value; productIds.push(row.values[2]); // 假设Sub Product是第3个字段(索引2) subsidiaryIds.push(row.values[3]); // 假设Subsidiary是第4个字段(索引3) rows.push(row); return true; }); // 批量获取名称映射 const productNameMap = getRecordNameMap(record.Type.INVENTORY_ITEM, productIds); const subsidiaryNameMap = getRecordNameMap(record.Type.SUBSIDIARY, subsidiaryIds); // 3. 转换结果为可读文本并构造CSV内容 let csvContent = "PO Status,Billing Status,Sub Product,Subsidiary\n"; rows.forEach(row => { const poStatus = poStatusMap[row.values[0]] || row.values[0]; const billingStatus = billingStatusMap[row.values[1]] || row.values[1]; const subProduct = productNameMap[row.values[2]] || row.values[2]; const subsidiary = subsidiaryNameMap[row.values[3]] || row.values[3]; // 处理带特殊字符的内容,避免CSV格式错误 csvContent += `"${poStatus.replace(/"/g, '""')}","${billingStatus.replace(/"/g, '""')}","${subProduct.replace(/"/g, '""')}","${subsidiary.replace(/"/g, '""')}"\n`; }); // 4. 创建CSV文件 const csvFile = file.create({ name: 'Dataset_Results.csv', fileType: file.Type.CSV, contents: csvContent, folder: -15 // 临时文件夹,可替换为实际文件夹ID }); const fileId = csvFile.save(); // 5. 发送邮件(请替换为实际收件人ID) const senderId = runtime.getCurrentUser().id; const recipientIds = [123]; email.send({ author: senderId, recipients: recipientIds, subject: 'NetSuite数据集结果CSV', body: '附件是最新的数据集结果,请查收。', attachments: [fileId] }); } // 辅助函数:获取枚举字段选项映射 function getEnumOptions(recordType, fieldId) { const field = record.getField({ type: recordType, fieldId: fieldId }); const optionMap = {}; field.options.forEach(opt => { optionMap[opt.value] = opt.text; }); return optionMap; } // 辅助函数:批量获取关联记录名称映射 function getRecordNameMap(recordType, ids) { const nameMap = {}; if (!ids || ids.length === 0) return nameMap; const uniqueIds = [...new Set(ids)]; const searchObj = search.create({ type: recordType, filters: [['internalid', 'anyof', uniqueIds]], columns: ['internalid', 'name'] }); searchObj.run().each(result => { const id = result.getValue('internalid'); const name = result.getValue('name'); nameMap[id] = name; return true; }); return nameMap; } return { execute: execute }; });
内容的提问来源于stack exchange,提问作者Maira S
相关产品推荐
相关产品推荐

