Skip to main content

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.12024-05-01
雷斯特莱特2.12024-05-01
Suitelet2.12024-05-01
MapReduceScript2.12024-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":

JsLibraryConfig.json
{
"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":

JsLibraryConfig.json
{
"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:

  1. 使用 file.load[^7] 从文件柜中提取文件。该方法返回一个代表文件元数据的 file.File 对象。

    ¥Pull files from the file cabinet using file.load[^7]. The method returns a file.File object which represents the file metadata.

  2. 使用 File#getContents[^8] 从文件中读取原始数据。该方法以 Base64 编码字符串形式返回数据。

    ¥Read raw data from the file using File#getContents[^8]. The method returns the data as a Base64-encoded string.

  3. 使用 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:

  1. 使用 SheetJS write 方法 [^11] 写入数据。使用 base64 输出类型 [^12],该方法将返回 Base64 字符串。

    ¥Write the data with the SheetJS write method[^11]. Using the base64 output type[^12], the method will return a Base64 string.

  2. 使用 file.create[^13] 创建一个新文件。推荐的文件类型是 file.Type.EXCEL。该方法返回一个 file.File 对象。

    ¥Create a new file using file.create[^13]. The recommended file type is file.Type.EXCEL. The method returns a file.File object.

  3. 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:

  1. request.files 对象.[^20] 中提取文件。对象中的每个值都是一个代表文件元数据的 file.File 对象。

    ¥Pull files from the request.files object.[^20]. Each value in the object is a file.File object which represents the file metadata.

  2. 使用 File#getContents[^21] 从文件中读取原始数据。该方法以 Base64 编码字符串形式返回数据。

    ¥Read raw data from the file using File#getContents[^21]. The method returns the data as a Base64-encoded string.

  3. 使用 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
});

由于文件字段的 iduploaded_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:

  1. 使用 SheetJS write 方法 [^24] 写入数据。使用 base64 输出类型 [^25],该方法将返回 Base64 字符串。

    ¥Write the data with the SheetJS write method[^24]. Using the base64 output type[^25], the method will return a Base64 string.

  2. 使用 file.create[^26] 创建一个新文件。推荐的文件类型是 file.Type.EXCEL。该方法返回一个 file.File 对象。

    ¥Create a new file using file.create[^26]. The recommended file type is file.Type.EXCEL. The method returns a file.File object.

  3. 使用 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 于 "读取文件"

¥See read in "Reading Files"

[^10]: 见 "实用函数"

¥See "Utility Functions"

[^11]: 见 write 于 "写入文件"

¥See write in "Writing Files"

[^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 于 "读取文件"

¥See read in "Reading Files"

[^23]: 见 "实用函数"

¥See "Utility Functions"

[^24]: 见 write 于 "写入文件"

¥See write in "Writing Files"

[^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.