Skip to main content

Project Structure

This guide explains every file and folder in an SDF project and their purposes.


Complete Project Structure

📁 my-netsuite-project/

├── 📄 suitecloud.config.js # Project configuration
├── 📄 manifest.xml # Deployment manifest
├── 📄 deploy.xml # Deployment settings (optional)
├── 📄 package.json # Node.js dependencies

├── 📁 src/ # Source files
│ │
│ ├── 📁 FileCabinet/ # Files deployed to File Cabinet
│ │ │
│ │ └── 📁 SuiteScripts/ # Script files
│ │ ├── 📁 Suitelets/ # Suitelet scripts
│ │ ├── 📁 UserEvents/ # User Event scripts
│ │ ├── 📁 ClientScripts/ # Client scripts
│ │ ├── 📁 ScheduledScripts/ # Scheduled scripts
│ │ ├── 📁 MapReduce/ # Map/Reduce scripts
│ │ ├── 📁 RESTlets/ # RESTlet scripts
│ │ ├── 📁 WorkflowActions/ # Workflow Action scripts
│ │ └── 📁 Libraries/ # Shared modules
│ │
│ ├── 📁 Objects/ # Custom object definitions
│ │ ├── customrecord_*.xml # Custom records
│ │ ├── customlist_*.xml # Custom lists
│ │ ├── custworkflow_*.xml # Workflows
│ │ ├── customsearch_*.xml # Saved searches
│ │ ├── customscript_*.xml # Script deployments
│ │ └── ... # Other objects
│ │
│ └── 📁 Templates/ # HTML templates
│ └── *.html # Template files

├── 📁 node_modules/ # Dependencies (auto-generated)

└── 📁 .git/ # Git version control

How Files Connect

┌─────────────────────────────────────────────────────────────────────────────┐
│ HOW SDF FILES CONNECT │
└─────────────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────────────┐
│ manifest.xml │
│ (What features are required) │
└────────────────────────────────┬────────────────────────────────────────────┘

┌────────────┴────────────┐
│ │
▼ ▼
┌─────────────────────────────┐ ┌─────────────────────────────┐
│ Script File (.js) │ │ Object File (.xml) │
│ my_suitelet.js │ │ customscript_my_sl.xml │
│ (Contains the code) │ │ (Deployment settings) │
└─────────────────────────────┘ └─────────────────────────────┘
│ │
│ Links to │
└────────────┬────────────┘


┌─────────────────────────────────────────────────────────────────────────────┐
│ NetSuite Account │
│ Script is deployed and accessible at configured URL/trigger │
└─────────────────────────────────────────────────────────────────────────────┘

Core Configuration Files

suitecloud.config.js

Project-level SDF configuration:

module.exports = {
// Where source files are located
defaultProjectFolder: 'src',

// Custom command configurations (optional)
commands: {
// Command-specific settings
}
};

manifest.xml

Defines project metadata and dependencies:

<?xml version="1.0" encoding="UTF-8"?>
<manifest projecttype="ACCOUNTCUSTOMIZATION">
<!-- Project name -->
<projectname>my-netsuite-project</projectname>

<!-- SDF framework version -->
<frameworkversion>1.0</frameworkversion>

<!-- Required NetSuite features -->
<dependencies>
<features>
<feature required="true">CUSTOMRECORDS</feature>
<feature required="true">SERVERSIDESCRIPTING</feature>
<feature required="true">CLIENTSIDESCRIPTING</feature>
</features>
</dependencies>
</manifest>

Common Features

FeaturePurpose
CUSTOMRECORDSCustom record types
SERVERSIDESCRIPTINGServer-side scripts
CLIENTSIDESCRIPTINGClient scripts
WORKFLOWWorkflow engine
ADVANCEDPRINTINGPDF generation

deploy.xml (Optional)

Controls which objects to deploy:

<?xml version="1.0" encoding="UTF-8"?>
<deploy>
<configuration>
<path>~/Objects/*</path>
</configuration>
<files>
<path>~/FileCabinet/SuiteScripts/*</path>
</files>
<objects>
<path>~/Objects/customscript_*.xml</path>
<path>~/Objects/customrecord_*.xml</path>
</objects>
</deploy>

Script Files

Location

All scripts go under src/FileCabinet/SuiteScripts/:

📁 SuiteScripts/
├── 📁 Suitelets/
│ └── invoice_report_sl.js
├── 📁 UserEvents/
│ └── sales_order_ue.js
├── 📁 ClientScripts/
│ └── customer_form_cs.js
└── 📁 Libraries/
└── common_utils.js

Script File Structure

/**
* @NApiVersion 2.1
* @NScriptType Suitelet
* @NModuleScope SameAccount
*/
define(['N/ui/serverWidget', 'N/search'],
(serverWidget, search) => {

/**
* Main entry point
* @param {Object} context
*/
const onRequest = (context) => {
// Script logic here
};

return { onRequest };
});

Script Type Headers

Script Type@NScriptType Value
SuiteletSuitelet
User EventUserEventScript
Client ScriptClientScript
ScheduledScheduledScript
Map/ReduceMapReduceScript
RESTletRestlet
Workflow ActionWorkflowActionScript

Object Files (XML)

Location

All objects go under src/Objects/:

📁 Objects/
├── customrecord_invoice_approval.xml # Custom record
├── customlist_approval_status.xml # Custom list
├── customscript_invoice_sl.xml # Script deployment
├── customsearch_pending_invoices.xml # Saved search
└── custworkflow_invoice_approval.xml # Workflow

Script Deployment XML

Links a script file to NetSuite:

<?xml version="1.0" encoding="UTF-8"?>
<customscript scriptid="customscript_invoice_sl">
<name>Invoice Report Suitelet</name>
<scripttype>SUITELET</scripttype>
<scriptfile>[/SuiteScripts/Suitelets/invoice_report_sl.js]</scriptfile>
<description>Invoice reporting suitelet</description>
<isinactive>F</isinactive>
<notifyowner>T</notifyowner>

<!-- Deployment configuration -->
<scriptdeployments>
<scriptdeployment scriptid="customdeploy_invoice_sl">
<status>RELEASED</status>
<title>Invoice Report</title>
<isdeployed>T</isdeployed>
<loglevel>DEBUG</loglevel>
<allroles>T</allroles>
</scriptdeployment>
</scriptdeployments>
</customscript>

Script Deployment Flow

┌─────────────────────────────────────────────────────────────────────────────┐
│ SCRIPT DEPLOYMENT RELATIONSHIP │
└─────────────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────┐
│ invoice_report_sl.js │
│ (Script Code) │
│ │
│ • Business logic │
│ • Form creation │
│ • Data processing │
└─────────────┬───────────────┘

│ Referenced by

┌─────────────────────────────┐
│ customscript_invoice_sl │
│ (Script Record) │
│ │
│ • Script type: SUITELET │
│ • Script file path │
│ • Owner, notifications │
└─────────────┬───────────────┘

│ Contains

┌─────────────────────────────┐
│ customdeploy_invoice_sl │
│ (Script Deployment) │
│ │
│ • URL/trigger settings │
│ • Role permissions │
│ • Status (Testing/Release) │
│ • Log level │
└─────────────────────────────┘

Custom Record XML

<?xml version="1.0" encoding="UTF-8"?>
<customrecordtype scriptid="customrecord_invoice_approval">
<recordname>Invoice Approval</recordname>
<includename>T</includename>
<showid>T</showid>

<customrecordcustomfields>
<customrecordcustomfield scriptid="custrecord_ia_invoice">
<label>Invoice</label>
<fieldtype>SELECT</fieldtype>
<selectrecordtype>-30</selectrecordtype>
</customrecordcustomfield>

<customrecordcustomfield scriptid="custrecord_ia_status">
<label>Status</label>
<fieldtype>SELECT</fieldtype>
<selectrecordtype>[customlist_approval_status]</selectrecordtype>
</customrecordcustomfield>

<customrecordcustomfield scriptid="custrecord_ia_notes">
<label>Approval Notes</label>
<fieldtype>TEXTAREA</fieldtype>
</customrecordcustomfield>
</customrecordcustomfields>
</customrecordtype>

Custom List XML

<?xml version="1.0" encoding="UTF-8"?>
<customlist scriptid="customlist_approval_status">
<name>Approval Status</name>

<customvalues>
<customvalue scriptid="val_pending">
<value>Pending</value>
</customvalue>
<customvalue scriptid="val_approved">
<value>Approved</value>
</customvalue>
<customvalue scriptid="val_rejected">
<value>Rejected</value>
</customvalue>
</customvalues>
</customlist>

Saved Search XML

<?xml version="1.0" encoding="UTF-8"?>
<customsearch scriptid="customsearch_pending_invoices">
<definition>
<searchtype>Transaction</searchtype>
<searchcolumns>
<column>
<name>tranid</name>
</column>
<column>
<name>entity</name>
</column>
<column>
<name>amount</name>
</column>
</searchcolumns>
<searchfilters>
<filter>
<name>type</name>
<operator>is</operator>
<values>VendBill</values>
</filter>
<filter>
<name>status</name>
<operator>is</operator>
<values>open</values>
</filter>
</searchfilters>
</definition>
</customsearch>

Templates Folder

HTML Templates

For Suitelets with custom HTML:

📁 Templates/
├── invoice_report.html
└── email_template.html

Loading Templates in Script

const file = require('N/file');

const template = file.load({
id: './Templates/invoice_report.html'
}).getContents();

File Naming Conventions

TypeConventionExample
Suitelet{name}_sl.jsinvoice_report_sl.js
User Event{name}_ue.jssales_order_ue.js
Client Script{name}_cs.jscustomer_cs.js
Scheduled{name}_ss.jsdaily_report_ss.js
Map/Reduce{name}_mr.jsdata_import_mr.js
RESTlet{name}_rl.jscustomer_api_rl.js
Library{name}_lib.jscommon_utils_lib.js
Custom Recordcustomrecord_{name}.xmlcustomrecord_approval.xml
Custom Listcustomlist_{name}.xmlcustomlist_status.xml
Script Deploycustomscript_{name}.xmlcustomscript_report_sl.xml

Best Practices

Folder Organization

📁 SuiteScripts/
├── 📁 Suitelets/
│ ├── 📁 Reports/ # Group by function
│ │ ├── sales_report_sl.js
│ │ └── inventory_report_sl.js
│ └── 📁 Forms/
│ └── data_entry_sl.js
├── 📁 UserEvents/
│ ├── 📁 SalesOrder/ # Group by record type
│ │ └── so_validation_ue.js
│ └── 📁 Invoice/
│ └── inv_automation_ue.js
└── 📁 Libraries/
├── error_handler_lib.js # Shared utilities
├── format_lib.js
└── search_lib.js

Object Naming

  • Use lowercase with underscores
  • Include type indicator: customrecord_, customlist_, etc.
  • Be descriptive: customrecord_invoice_approval not customrecord_ia

Import Existing Objects

To import existing objects from NetSuite:

Ctrl+Shift+P → SuiteCloud: Import Objects

Or via terminal:

suitecloud object:import --type customrecord
suitecloud object:import --type savedsearch
suitecloud object:import --type workflow

Next Steps