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 | 2024-05-01 |
雷斯特莱特 | 2.1 | 2024-05-01 |
Suitelet | 2.1 | 2024-05-01 |
MapReduceScript | 2.1 | 2024-05-01 |
问题跟踪器中的 请参阅问题#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
。参考可以是绝对的或相对的 [^4]。
¥Assuming the uploaded file was named xlsx.full.min.js
, the paths
object in
the JSON configuration should reference xlsx.full.min
. The reference can be
absolute or relative[^4].
例如,如果脚本 xlsx.full.min.js
放置在 SuiteScripts
顶层目录中,则配置应使用 "/SuiteScripts/xlsx.full.min"
:
¥For example, if the script xlsx.full.min.js
was placed in the SuiteScripts
top-level directory, the config should use "/SuiteScripts/xlsx.full.min"
:
{
"paths": {
"xlsx": "/SuiteScripts/xlsx.full.min"
}
}
还支持相对引用。如果整个项目存储在一个文件夹中,则配置可以使用 "./xlsx.full.min"
:
¥Relative references are also supported. If the entire project is stored in one
folder, the config can use "./xlsx.full.min"
:
{
"paths": {
"xlsx": "./xlsx.full.min"
}
}
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.File
object 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
read
method[^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
write
method[^11]. Using thebase64
output 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.File
object. -
用
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.files
object.[^20]. Each value in the object is afile.File
object 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
read
method[^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 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
write
method[^24]. Using thebase64
output 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.File
object. -
使用
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)
。
¥See ServerResponse.writeFile(options)
in the NetSuite documentation.
[^20]: 请参阅 NetSuite 文档中的 ServerRequest.files
。
¥See ServerRequest.files
in the NetSuite documentation.
[^21]: 请参阅 NetSuite 文档中的 File.getContents()
。
¥See File.getContents()
in the NetSuite documentation.
[^22]: 见 read
于 "读取文件"
[^23]: 见 "实用函数"
¥See "Utility Functions"
[^24]: 见 write
于 "写入文件"
[^25]: 见 "支持的输出格式"
¥See "Supported Output Formats"
[^26]: 请参阅 NetSuite 文档中的 file.create(options)
。
¥See file.create(options)
in the NetSuite documentation.
[^27]: 请参阅 NetSuite 文档中的 ServerResponse.writeFile(options)
。
¥See ServerResponse.writeFile(options)
in the NetSuite documentation.