13 KiB
title | sidebar_label | description | pagination_prev | pagination_next |
---|---|---|---|---|
Spreadsheets in NetSuite SuiteScripts | NetSuite | Automate the NetSuite ERP platform with SuiteScripts. Effortlessly read and write spreadsheets using SheetJS. Modernize Excel-powered business processes with confidence. | demos/local/index | demos/extensions/index |
import current from '/version.js'; import CodeBlock from '@theme/CodeBlock';
NetSuite is a suite of cloud-based software systems for Enterprise Resource Planning (ERP). It has a robust scripting interface.1
SheetJS is a JavaScript library for reading and writing data from spreadsheets.
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.
:::note Tested Deployments
This demo was verified by NetSuite consultants in the following deployments:
@NScriptType |
@NApiVersion |
Date |
---|---|---|
ScheduledScript | 2.1 | 2024-12-06 |
Restlet | 2.1 | 2024-12-06 |
Suitelet | 2.1 | 2024-12-06 |
MapReduceScript | 2.1 | 2024-12-06 |
:::
:::info pass
See issue #3058 in the issue tracker for more examples submitted by NetSuite consultants.
:::
Installation
In SuiteScript parlance, third-party scripts are "Custom Modules"2.
The SheetJS AMD script can be uploaded
to the file cabinet and referenced in the define
call in SuiteScripts.
:::info pass
SheetJS scripts have been tested against the Rhino JavaScript engine3 and work in both SuiteScript 2.0 and SuiteScript 2.1 deployments.
:::
Adding SheetJS Scripts
The SheetJS standalone script should be uploaded to the File Cabinet.
:::note pass
It is strongly recommended to keep the original filename xlsx.full.min.js
.
:::
JSON Configuration
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 relative4.
:::note pass
In older versions of SuiteScript, references should omit the .js
extension.
:::
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": {
// highlight-next-line
"xlsx": "/SuiteScripts/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": {
// highlight-next-line
"xlsx": "./xlsx.full.min.js"
}
}
SuiteScript Usage
The JSON configuration file should be referenced in SuiteScripts using
@NAmdConfig
. The path alias "xlsx"
should be passed to define
:
/**
* @NApiVersion 2.x
// highlight-next-line
* @NAmdConfig ./JsLibraryConfig.json
* ... more options ...
*/
// highlight-next-line
define(['N/file', 'xlsx'], function(file, XLSX) {
// ... use XLSX here ...
});
Sheets in the File Cabinet
The NetSuite File Cabinet5 is the primary feature for storing documents.
N/file
is the primary module for interacting with the File Cabinet6.
This section assumes that N/file
is bound to the variable file
:
define(
['N/file', 'xlsx'],
function(
// highlight-next-line
file, // 'N/file'
XLSX // 'xlsx'
) {
// ...
}
);
Reading Files
flowchart LR
subgraph NetSuite Operations
cab[(File\nCabinet)]
file(File)
base64{{Base64\nString}}
end
wb(((SheetJS\nWorkbook)))
cab --> |`load`\nN/file| file
file --> |`getContents`\nFile method| base64
base64 --> |`read`\nSheetJS| wb
There are three steps to reading files:
-
Pull files from the file cabinet using
file.load
7. The method returns afile.File
object which represents the file metadata. -
Read raw data from the file using
File#getContents
8. The method returns the data as a Base64-encoded string. -
Parse the data with the SheetJS
read
method9. This method returns a SheetJS workbook object.
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" });
At this point, standard SheetJS utility functions10 can extract data from the workbook object.
Writing Files
flowchart LR
wb(((SheetJS\nWorkbook)))
subgraph NetSuite Operations
base64{{Base64\nString}}
file(File)
cab[(File\nCabinet)]
end
wb --> |`write`\nSheetJS| base64
base64 --> |`create`\nN/file| file
file --> |`save`\nFile method| cab
There are three steps to writing files:
-
Write the data with the SheetJS
write
method11. Using thebase64
output type12, the method will return a Base64 string. -
Create a new file using
file.create
13. The recommended file type isfile.Type.EXCEL
. The method returns afile.File
object. -
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();
Sheets in Suitelet Requests
Suitelets are driven by an exported onRequest
method15.
The request
property of the argument is a ServerRequest
object16. The
files
property of the request17 is an object whose values are file
objects.
The response
property of the argument is a ServerResponse
object18. The
writeFile
method19 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
flowchart LR
subgraph NetSuite Operations
req((Suitelet\nRequest))
file(File)
base64{{Base64\nString}}
end
wb(((SheetJS\nWorkbook)))
req --> |`files`\nrequest data| file
file --> |`getContents`\nFile method| base64
base64 --> |`read`\nSheetJS| wb
There are three steps to importing data from Suitelet requests:
-
Pull files from the
request.files
object.20. Each value in the object is afile.File
object which represents the file metadata. -
Read raw data from the file using
File#getContents
21. The method returns the data as a Base64-encoded string. -
Parse the data with the SheetJS
read
method22. 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" });
At this point, standard SheetJS utility functions23 can extract data from the workbook object.
:::note pass
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({
// highlight-next-line
id: "uploaded_file",
label: "Choose Spreadsheet",
type: serverWidget.FieldType.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
flowchart LR
wb(((SheetJS\nWorkbook)))
subgraph NetSuite Operations
base64{{Base64\nString}}
file(File)
res[(Suitelet\nResponse)]
end
wb --> |`write`\nSheetJS| base64
base64 --> |`create`\nN/file| file
file --> |`writeFile`\nResponse method| res
There are three steps to generating downloadable files:
-
Write the data with the SheetJS
write
method24. Using thebase64
output type25, the method will return a Base64 string. -
Create a new file using
file.create
26. The recommended file type isfile.Type.EXCEL
. The method returns afile.File
object. -
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
NetSuite users reported errors using SheetJS scripts:
Fail to evaluate script: com.netsuite.suitescript.scriptobject.GraalValueAdapter@68d0f09d
NetSuite is incorrectly treating xlsx
as a reserved word. As this behavior is
not documented, it is believed to be a NetSuite bug.
Exasperated users concluded that Oracle will not be addressing this bug:
Oracle is not going to do anything with this
The "Oracle Bugs" warning in the NetSuite installation page includes a workaround that involves manually patching the library.
-
See "SuiteScript 2.x API Introduction" in the NetSuite documentation. ↩︎
-
See "SuiteScript 2.x Custom Modules" in the NetSuite documentation. ↩︎
-
See "Module Dependency Paths" in the NetSuite documentation. ↩︎
-
See "File Cabinet Overview" in the NetSuite documentation. ↩︎
-
See
N/file
Module in the NetSuite documentation. ↩︎ -
See
File.getContents()
in the NetSuite documentation. ↩︎ -
See
file.create(options)
in the NetSuite documentation. ↩︎ -
See
File.save()
in the NetSuite documentation. ↩︎ -
See
onRequest(params)
in the NetSuite documentation. ↩︎ -
See
http.ServerRequest
in the NetSuite documentation. ↩︎ -
See
ServerRequest.files
in the NetSuite documentation. ↩︎ -
See
http.ServerResponse
in the NetSuite documentation. ↩︎ -
See
ServerResponse.writeFile(options)
in the NetSuite documentation. ↩︎ -
See
ServerRequest.files
in the NetSuite documentation. ↩︎ -
See
File.getContents()
in the NetSuite documentation. ↩︎ -
See
file.create(options)
in the NetSuite documentation. ↩︎ -
See
ServerResponse.writeFile(options)
in the NetSuite documentation. ↩︎