π¦ Business Use Case
Your eCommerce site or WMS (Warehouse Management System) tracks product inventory changes β but your NetSuite item records are out of sync. You need a way to push updated stock quantities to NetSuite in real time, automatically, and securely.
π Solution: RESTlet for Inventory Updates
Instead of manual CSV imports or scheduled Map/Reduce jobs, use a RESTlet that:
- Accepts a JSON payload from your external system (e.g., WMS, Shopify, or middleware).
- Updates Inventory Detail or Quantity On Hand on Inventory Items, by location.
- Returns a clean API response with status and error handling.
π§ RESTlet Design Pattern
- Accepts:
POST /app/site/hosting/restlet.nl?script=xxx&deploy=1 - Payload:
{
"external_id": "sku-abc123",
"location_id": 3,
"quantity_on_hand": 48
}
- Response:
{
"success": true,
"item_id": 1278,
"location_id": 3,
"quantity_set": 48
}
π§© SuiteScript 2.1: RESTlet to Sync Inventory
/**
* @NApiVersion 2.1
* @NScriptType Restlet
* @author The NetSuite Pro
* @description Updates inventory quantity for a given item and location
*/
define(['N/record', 'N/search', 'N/log'], (record, search, log) => {
function post(request) {
const { external_id, location_id, quantity_on_hand } = request;
if (!external_id || !location_id || typeof quantity_on_hand !== 'number') {
return { success: false, error: 'Missing required parameters' };
}
try {
// Lookup inventory item by External ID (or use custom field if needed)
const itemId = getItemIdByExternalId(external_id);
if (!itemId) return { success: false, error: `Item not found for external_id: ${external_id}` };
// Load the Inventory Item
const rec = record.load({
type: record.Type.INVENTORY_ITEM,
id: itemId,
isDynamic: true
});
// Go to 'Locations' sublist and find the line for the given location
const lineCount = rec.getLineCount({ sublistId: 'locations' });
let lineIndex = -1;
for (let i = 0; i < lineCount; i++) {
const loc = rec.getSublistValue({ sublistId: 'locations', fieldId: 'location', line: i });
if (Number(loc) === Number(location_id)) {
lineIndex = i;
break;
}
}
if (lineIndex === -1) return { success: false, error: 'Location not assigned to item' };
rec.setSublistValue({
sublistId: 'locations',
fieldId: 'quantityonhand',
line: lineIndex,
value: quantity_on_hand
});
const id = rec.save({ enableSourcing: true, ignoreMandatoryFields: false });
return { success: true, item_id: id, location_id, quantity_set: quantity_on_hand };
} catch (e) {
log.error('Inventory Sync Error', e.message);
return { success: false, error: e.message };
}
}
function getItemIdByExternalId(external_id) {
const results = search.lookupFields({
type: search.Type.ITEM,
id: external_id,
columns: ['internalid']
});
return results && results.internalid && results.internalid[0] && results.internalid[0].value;
}
return { post };
});
π Authentication & Security Tips
- Use Token-Based Authentication (TBA) or OAuth 2.0.
- Restrict to specific roles with permission to only update Inventory Items.
- Implement API key validation if calling from an external server.
- Log all calls using
N/logfor traceability. - Optional: create a custom record log of updates.
π§ͺ Test Example
Use Postman or curl to simulate:
curl -X POST \
https://ACCOUNT_ID.restlets.api.netsuite.com/app/site/hosting/restlet.nl?script=1234&deploy=1 \
-H "Authorization: NLAuth ..." \
-H "Content-Type: application/json" \
-d '{
"external_id": "sku-abc123",
"location_id": 3,
"quantity_on_hand": 48
}'
π Best Practices
- Donβt overwrite committed or on-order quantities β only use for real-time on-hand values.
- If you use multiple locations, make sure the item record has that location enabled.
- Use a middleware queue (like Boomi, Celigo, or AWS Lambda) for high-volume systems.
- Add retry/backoff logic if you expect concurrency issues.
- Use SuiteQL if you prefer pulling inventory values from NetSuite instead of pushing.
Leave a Reply