NetSuite SuiteScripts 中的电子表格
NetSuite 是一套基于云的企业资源规划 (ERP) 软件系统。它有一个强大的脚本接口。[^1]
¥NetSuite is a suite of cloud-based software systems for Enterprise Resource Planning (ERP). It has a robust scripting interface.[^1]
SheetJS 是一个用于从电子表格读取和写入数据的 JavaScript 库。
¥SheetJS is a JavaScript library for reading and writing data from spreadsheets.
该演示探讨了 NetSuite 中的 SuiteScript 脚本功能。我们将探讨如何在 SuiteScripts 中使用 SheetJS 在 NetSuite 中读取和写入文件。
¥This demo explores the SuiteScript scripting features in NetSuite. We'll explore how to use SheetJS in SuiteScripts for reading and writing files in NetSuite.
该演示已由 NetSuite 顾问在以下部署中进行了验证:
¥This demo was verified by NetSuite consultants in the following deployments:
| @NScriptType | @NApiVersion | 日期 | 
|---|---|---|
| 预定脚本 | 2.1 | 2025-06-08 | 
| 雷斯特莱特 | 2.1 | 2025-06-08 | 
| Suitelet | 2.1 | 2025-06-08 | 
| MapReduceScript | 2.1 | 2025-06-08 | 
问题跟踪器中的 请参阅问题#3058 可获取 NetSuite 顾问提交的更多示例。
¥See issue #3058 in the issue tracker for more examples submitted by NetSuite consultants.
安装
¥Installation
用 SuiteScript 的话说,第三方脚本是 "定制模块"[^2]。
¥In SuiteScript parlance, third-party scripts are "Custom Modules"[^2].
SheetJS AMD 脚本 可以上传到文件柜并在 SuiteScripts 中的 define 调用中引用。
¥The SheetJS AMD script can be uploaded
to the file cabinet and referenced in the define call in SuiteScripts.
SheetJS 脚本已经针对 Rhino JavaScript 引擎 [^3] 进行了测试,并且可以在 SuiteScript 2.0 和 SuiteScript 2.1 部署中工作。
¥SheetJS scripts have been tested against the Rhino JavaScript engine[^3] and work in both SuiteScript 2.0 and SuiteScript 2.1 deployments.
添加 SheetJS 脚本
¥Adding SheetJS Scripts
The SheetJS standalone script should be uploaded to the File Cabinet.
强烈建议保留原始文件名 xlsx.full.min.js。
¥It is strongly recommended to keep the original filename xlsx.full.min.js.
JSON 配置
¥JSON Configuration
假设上传的文件名为 xlsx.full.min.js,则 JSON 配置中的 paths 对象应引用 xlsx.full.min.js。参考可以是绝对的或相对的 [^4]。
¥Assuming the uploaded file was named xlsx.full.min.js, the paths object in
the JSON configuration should reference xlsx.full.min.js. The reference can be
absolute or relative[^4].
在较旧版本的 SuiteScript 中,引用应省略 .js 扩展名。
¥In older versions of SuiteScript, references should omit the .js extension.
例如,如果脚本 xlsx.full.min.js 放在 SuiteScripts 顶层文件夹中,则应将 xlsx 映射到 /SuiteScripts/xlsx.full.min.js:
¥For example, if the script xlsx.full.min.js was placed in the SuiteScripts
top-level folder, xlsx should be mapped to /SuiteScripts/xlsx.full.min.js:
{
  "paths": {
    "xlsx": "/SuiteScripts/xlsx.full.min.js"
  }
}
还支持相对引用。如果整个项目存储在一个文件夹中,则应将 xlsx 映射到 "./xlsx.full.min.js":
¥Relative references are also supported. If the entire project is stored in one
folder, xlsx should be mapped to "./xlsx.full.min.js":
{
  "paths": {
    "xlsx": "./xlsx.full.min.js"
  }
}
SuiteScript 用法
¥SuiteScript Usage
JSON 配置文件应使用 @NAmdConfig 在 SuiteScripts 中引用。路径别名 "xlsx" 应传递给 define:
¥The JSON configuration file should be referenced in SuiteScripts using
@NAmdConfig. The path alias "xlsx" should be passed to define:
/**
* @NApiVersion 2.x
* @NAmdConfig  ./JsLibraryConfig.json
* ... more options ...
*/
define(['N/file', 'xlsx'], function(file, XLSX) {
  // ... use XLSX here ...
});
文件柜中的纸张
¥Sheets in the File Cabinet
NetSuite 文件柜 [^5] 是存储文档的主要功能。
¥The NetSuite File Cabinet[^5] is the primary feature for storing documents.
N/file 是与文件柜 [^6] 交互的主要模块。本节假设 N/file 绑定到变量 file:
¥N/file is the primary module for interacting with the File Cabinet[^6].
This section assumes that N/file is bound to the variable file:
define(
  ['N/file', 'xlsx'],
  function(
    file, // 'N/file'
    XLSX  // 'xlsx'
  ) {
    // ...
  }
);
读取文件
¥Reading Files
读取文件分为三个步骤:
¥There are three steps to reading files:
- 
使用 file.load[^7] 从文件柜中提取文件。该方法返回一个代表文件元数据的file.File对象。¥Pull files from the file cabinet using file.load[^7]. The method returns afile.Fileobject which represents the file metadata.
- 
使用 File#getContents[^8] 从文件中读取原始数据。该方法以 Base64 编码字符串形式返回数据。¥Read raw data from the file using File#getContents[^8]. The method returns the data as a Base64-encoded string.
- 
使用 SheetJS read方法 [^9] 解析数据。此方法返回一个 SheetJS 工作簿对象。¥Parse the data with the SheetJS readmethod[^9]. This method returns a SheetJS workbook object.
file.load 需要 id 属性,该属性可以是内部 ID(显示在文件柜 Web 界面中)或绝对或相对路径字符串。
¥file.load expects an id property, which can be the internal ID (displayed in
the File Cabinet web interface) or an absolute or relative path string.
/* file ID or path */
var id_of_file = 7262; // Internal ID 7262
/* load file */
var f = file.load({ id: id_of_file });
/* read file */
var b64 = f.getContents();
/* parse */
var workbook = XLSX.read(b64, { type: "base64" });
此时,标准 SheetJS 实用函数 [^10] 可以从工作簿对象中提取数据。
¥At this point, standard SheetJS utility functions[^10] can extract data from the workbook object.
写入文件
¥Writing Files
写入文件分为三个步骤:
¥There are three steps to writing files:
- 
使用 SheetJS write方法 [^11] 写入数据。使用base64输出类型 [^12],该方法将返回 Base64 字符串。¥Write the data with the SheetJS writemethod[^11]. Using thebase64output type[^12], the method will return a Base64 string.
- 
使用 file.create[^13] 创建一个新文件。推荐的文件类型是file.Type.EXCEL。该方法返回一个file.File对象。¥Create a new file using file.create[^13]. The recommended file type isfile.Type.EXCEL. The method returns afile.Fileobject.
- 
用 File#save[^14] 将数据上传到文件柜¥Upload data to the File Cabinet with File#save[^14]
/* write XLSX workbook as Base64 string */
var out = XLSX.write(workbook, { bookType: "xlsx", type: "base64" });
/* create file */
var newfile = file.create({
  name: 'SheetJSCabinetExport.xlsx', // replace with desired name
  fileType: file.Type.EXCEL,
  contents: out
});
/* save */
newfile.save();
Suitelet 请求中的工作表
¥Sheets in Suitelet Requests
Suitelet 由导出的 onRequest 方法 [^15] 驱动。
¥Suitelets are driven by an exported onRequest method[^15].
参数的 request 属性是 ServerRequest 对象 [^16]。request[^17] 的 files 属性是一个对象,其值为 file 对象。
¥The request property of the argument is a ServerRequest object[^16]. The
files property of the request[^17] is an object whose values are file objects.
参数的 response 属性是 ServerResponse 对象 [^18]。响应的 writeFile 方法 [^19] 可以用 file 对象响应。对于本节中的示例,参数将命名为 context:
¥The response property of the argument is a ServerResponse object[^18]. The
writeFile method[^19] of the response can respond with a file object.
For the examples in this section, the argument will be named context:
/**
 * @NApiVersion 2.1
 * @NAmdConfig ./JsLibraryConfig.json
 * @NScriptType Suitelet
 */
define(['N/file', 'xlsx'], function (file, XLSX) {
  function onRequest(context) {
    /* ServerRequest object */
    var request = context.request;
    /* ServerResponse object */
    var response = context.response;
    // ... do work here ...
  }
  return { onRequest: onRequest };
});
导入表格数据
¥Importing Sheet Data
从 Suitelet 请求导入数据分为三个步骤:
¥There are three steps to importing data from Suitelet requests:
- 
从 request.files对象.[^20] 中提取文件。对象中的每个值都是一个代表文件元数据的file.File对象。¥Pull files from the request.filesobject.[^20]. Each value in the object is afile.Fileobject which represents the file metadata.
- 
使用 File#getContents[^21] 从文件中读取原始数据。该方法以 Base64 编码字符串形式返回数据。¥Read raw data from the file using File#getContents[^21]. The method returns the data as a Base64-encoded string.
- 
使用 SheetJS read方法 [^22] 解析数据。此方法返回一个 SheetJS 工作簿对象。¥Parse the data with the SheetJS readmethod[^22]. This method returns a SheetJS workbook object.
/* form element ID or field name */
var id_of_file = "uploaded_file"
/* get file from request */
var f = context.request.files[id_of_file];
/* read file */
var b64 = f.getContents();
/* parse */
var workbook = XLSX.read(b64, { type: "base64" });
此时,标准 SheetJS 实用函数 [^23] 可以从工作簿对象中提取数据。
¥At this point, standard SheetJS utility functions[^23] can extract data from the workbook object.
当以编程方式使用 N/ui/serverWidget 创建表单时,files 对象的键由字段的 id 属性确定。
¥When programmatically creating a form with N/ui/serverWidget, the keys of the
files object are determined by the id properties of the field.
var form = serverWidget.createForm({ title: "Upload Spreadsheet" });
var field = form.addField({
  id: "uploaded_file",
  label: "Choose Spreadsheet",
  type: serverWidget.FieldType.FILE
});
由于文件字段的 id 是 uploaded_file,因此请求处理程序可以在 context.request.files["uploaded_file"] 处访问文件
¥Since the id of the file field is uploaded_file, the request handler can
access the file at context.request.files["uploaded_file"]
导出文件
¥Exporting Files
生成可下载文件分为三个步骤:
¥There are three steps to generating downloadable files:
- 
使用 SheetJS write方法 [^24] 写入数据。使用base64输出类型 [^25],该方法将返回 Base64 字符串。¥Write the data with the SheetJS writemethod[^24]. Using thebase64output type[^25], the method will return a Base64 string.
- 
使用 file.create[^26] 创建一个新文件。推荐的文件类型是file.Type.EXCEL。该方法返回一个file.File对象。¥Create a new file using file.create[^26]. The recommended file type isfile.Type.EXCEL. The method returns afile.Fileobject.
- 
使用 response.writeFile[^27] 启动下载。¥Initiate download with response.writeFile[^27].
/* write XLSX workbook as Base64 string */
var out = XLSX.write(workbook, { bookType: "xlsx", type: "base64" });
/* create file */
var newfile = file.create({
  name: 'SheetJSSuiteletExport.xlsx', // replace with desired name
  fileType: file.Type.EXCEL,
  contents: out
});
/* initiate download */
context.response.writeFile(newfile);
故障排除
¥Troubleshooting
使用 SheetJS 脚本时出现 NetSuite 用户报告 错误:
¥NetSuite users reported errors using SheetJS scripts:
Fail to evaluate script: com.netsuite.suitescript.scriptobject.GraalValueAdapter@68d0f09d
NetSuite 错误地将 xlsx 视为保留字。由于此行为没有记录,因此它被认为是 NetSuite 错误。
¥NetSuite is incorrectly treating xlsx as a reserved word. As this behavior is
not documented, it is believed to be a NetSuite bug.
激怒了 用户得出结论,Oracle 不会解决此错误:
¥Exasperated users concluded that Oracle will not be addressing this bug:
Oracle 不会对此采取任何行动
¥Oracle is not going to do anything with this
NetSuite 安装页面中的 "甲骨文错误" 警告包括一个解决方法,涉及手动修补库。
¥The "Oracle Bugs" warning in the NetSuite installation page includes a workaround that involves manually patching the library.
[^1]: 请参阅 NetSuite 文档中的 "SuiteScript 2.x API 简介"。
¥See "SuiteScript 2.x API Introduction" in the NetSuite documentation.
[^2]: 请参阅 NetSuite 文档中的 "SuiteScript 2.x 自定义模块"。
¥See "SuiteScript 2.x Custom Modules" in the NetSuite documentation.
[^3]: 见 "Java + Rhino" 演示
¥See "Java + Rhino" demo
[^4]: 请参阅 NetSuite 文档中的 "模块依赖路径"。
¥See "Module Dependency Paths" in the NetSuite documentation.
[^5]: 请参阅 NetSuite 文档中的 "文件柜概述"。
¥See "File Cabinet Overview" in the NetSuite documentation.
[^6]: 请参阅 NetSuite 文档中的 N/file 模块。
¥See N/file Module in the NetSuite documentation.
[^7]: 请参阅 NetSuite 文档中的 file.load。
¥See file.load in the NetSuite documentation.
[^8]: 请参阅 NetSuite 文档中的 File.getContents()。
¥See File.getContents() in the NetSuite documentation.
[^9]: 见 read 于 "读取文件"
[^10]: 见 "实用函数"
¥See "Utility Functions"
[^11]: 见 write 于 "写入文件"
[^12]: 见 "支持的输出格式"
¥See "Supported Output Formats"
[^13]: 请参阅 NetSuite 文档中的 file.create(options)。
¥See file.create(options) in the NetSuite documentation.
[^14]: 请参阅 NetSuite 文档中的 File.save()。
¥See File.save() in the NetSuite documentation.
[^15]: 请参阅 NetSuite 文档中的 onRequest(params)。
¥See onRequest(params) in the NetSuite documentation.
[^16]: 请参阅 NetSuite 文档中的 http.ServerRequest。
¥See http.ServerRequest in the NetSuite documentation.
[^17]: 请参阅 NetSuite 文档中的 ServerRequest.files。
¥See ServerRequest.files in the NetSuite documentation.
[^18]: 请参阅 NetSuite 文档中的 http.ServerResponse。
¥See http.ServerResponse in the NetSuite documentation.
[^19]: 请参阅 NetSuite 文档中的 ServerResponse.writeFile(options)