Here is a sample suitelet code that can add item by scanning the QR code and add this item item in the sales order table.
/**
* @NApiVersion 2.1
* @NScriptType Suitelet
*/
define(['N/record', 'N/file', 'N/search', 'N/url'],
/**
* @param{record} record
*/
(record, file, search, url) => {
"use strict";
const BACKGROUND_IMAGE_PATH = 'SuiteScripts/Jobin and Jismi IT ServicesLLP/CRYS-94 QR Code Scanning/layer.svg';
const LOGO_FILE_PATH = 'SuiteScripts/Jobin and Jismi IT ServicesLLP/CRYS-94 QR Code Scanning/CT-Logo-900x214.png';
const ITEM_HTML_PATH = "SuiteScripts/Jobin and Jismi IT ServicesLLP/CRYS-94 QR Code Scanning/jj_html_scan_item_qr_code_scanning_crys96.html";
const SCRIPT_ID_UPDATE_SO = 'customscript_jj_sl_so_details_crys_192';
const DEPLOY_ID_UPDATE_SO = 'customdeploy_jj_sl_so_details_crys_192';
function getsalesOrder(salesOrderId) {
try {
let salesOrderSearchObj = search.create({
type: "salesorder",
filters: [
["type", "anyof", "SalesOrd"],
"AND", ["internalid", "anyof", salesOrderId],
"AND", ["mainline", "is", "F"],
"AND", ["taxline", "is", "F"],
"AND", ["shipping", "is", "F"]
],
columns: [
search.createColumn({ name: "item", label: "Item" }),
search.createColumn({ name: "rate", label: "Item Rate" }),
search.createColumn({ name: "quantity", label: "Quantity" }),
search.createColumn({ name: "amount", label: "Amount" }),
search.createColumn({ name: "taxcode", label: "Tax Item" }),
search.createColumn({ name: "taxgroup", join: "taxItem", label: "Tax Group" }),
search.createColumn({ name: "rate", join: "taxItem", label: "Rate" }),
search.createColumn({ name: "entity", label: "Customer Name" }),
search.createColumn({
name: "custentity_jj_cus_member_category",
join: "customerMain",
label: "Membership Category"
})
]
});
let results = [];
let customerId = "", customerCategory = "";
salesOrderSearchObj.run().each(result => {
results.push({
itemName: result.getValue({ name: "item" }),
itemRate: result.getValue({ name: "rate" }),
itemQuantity: result.getValue({ name: "quantity" }),
itemAmount: result.getValue({ name: "amount" }),
taxgroup: result.getText({ name: "taxcode" }),
taxrate: result.getText({ name: "taxgroup", join: "taxItem",}),
})
if (!customerId) {
customerId = result.getText("entity");
customerCategory = result.getText({ name: "custentity_jj_cus_member_category", join: "customerMain" });
}
return true;
});
return { customerId, customerCategory, items: results }; // Return customer ID and items
} catch (error) {
log.error('error @getsalesOrder', error)
return { customerId: "", items: [] };
}
}
function salesOrderDetailsPage(scriptContext, backgroundFileUrl, fullFileUrl, salesOrderdetails, itemhtmlFileUrl, scanningPageUrl, itemLine) {
try {
if(itemLine)
{
log.debug('itemLine',itemLine)
}
let salesOrderJson = JSON.stringify(salesOrderdetails.items || []);
let customerId = salesOrderdetails.customerId || "N/A";
let customerCategory = salesOrderdetails.customerCategory || "N/A";
let htmlContent = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sales Order Details</title>
<style>
body {
font-family: 'Montserrat', sans-serif;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
flex-direction: column;
background: url('${backgroundFileUrl.replace(/"/g, '"')}') no-repeat center center/cover;
}
.container {
width: 90%;
max-width: 1200px;
min-height: 80vh;
margin: auto;
padding: 40px;
border-radius: 10px;
background: rgba(220, 220, 220, 0.25);
box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1);
}
.logo {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
margin-bottom: 10px;
}
.logo img {
max-width: 180px;
height: auto;
object-fit: contain;
}
h2 {
text-align: center;
margin-bottom: 20px;
font-size: 2rem;
}
table {
width: 100%;
margin-bottom: 20px;
border: 2px solid black;
}
th, td {
border: 2px solid #ddd;
padding: 10px;
text-align: center;
vertical-align: middle;
}
th {
background-color: #007bff;
color: white;
}
button {
padding: 10px 15px;
border: none;
cursor: pointer;
font-size: 14px;
margin-top: 10px;
transition: all 0.3s ease-in-out;
}
.remove-btn {
background-color: red;
color: white;
}
.add-btn {
background-color: green;
color: white;
}
.save-btn {
background-color: #007bff;
color: white;
}
</style>
</head>
<body>
<div class="container">
<div class="logo">
<img src="${fullFileUrl.replace(/"/g, '"')}" alt="Company Logo">
</div>
<h2>Sales Order Details</h2>
<p><strong>Sales Order ID:</strong> <span id="salesOrderId"></span></p>
<p><strong>Customer Name:</strong> <span id="customerId">${customerId}</span></p>
<p><strong>Customer Category:</strong> <span id="customerCategory">${customerCategory}</span></p>
<table id="salesOrderTable">
<thead>
<tr>
<th>Item Name</th>
<th>Quantity</th>
<th>Price</th>
<th>Tax Group</th>
<th>Tax Rate</th>
<th>Action</th>
</tr>
</thead>
<tbody id="orderItems"></tbody>
</table>
<button class="add-btn" onclick="addNewItem()">+ Add Item</button>
<button class="save-btn" onclick="saveOrder()">Save</button>
</div>
<script>
salesOrderId = new URLSearchParams(window.location.search).get("salesOrderId");
document.getElementById("salesOrderId").textContent = salesOrderId;
orderItems = ${salesOrderJson};
function renderOrderItems() {
let tableBody = document.getElementById("orderItems");
tableBody.innerHTML = "";
orderItems.forEach((item, index) => {
let row = `
<tr>
<td>${item.itemName}</td>
<td><input type="number" value="${item.itemQuantity}" min="1"></td>
<td>${item.itemRate}</td>
<td>${item.taxGroup}</td>
<td>${item.taxRate}</td>
<td><button class="remove-btn" onclick="removeItem(${index})">Remove</button></td>
</tr>`;
tableBody.innerHTML += row;
});
}
renderOrderItems();
function removeItem(index) {
orderItems.splice(index, 1);
renderOrderItems();
}
function itemscanning(fileUrl, suiteletUrl) {
try {
if (suiteletUrl) {
fileUrl += "?suiteletUrl=" + encodeURIComponent(suiteletUrl);
}
let scanWindow = window.open(fileUrl, "_blank", "width=650, height=650, left=400");
} catch (e) {
console.error("Error in itemscanning:", e);
}
}
function addNewItem() {
itemscanning("${itemhtmlFileUrl}", "${scanningPageUrl}");
}
async function getSuiteletUrl() {
try {
let urlParams = new URLSearchParams(window.location.search);
let itemInternalId = urlParams.get("itemInternalId");
console.log('itemInternalId',itemInternalId)
} catch (error) {
console.error("Error fetching item:", error);
}
}
async function saveOrder() {
try {
let response = await fetch("/updateSalesOrder", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ salesOrderId, items: orderItems })
});
let data = await response.json();
if (data.success) {
alert("Sales Order Updated Successfully!");
window.location.href = "/";
} else {
alert("Failed to update Sales Order.");
}
} catch (error) {
console.error("Error saving order:", error);
}
}
getSuiteletUrl();
if(${itemLine}){
console.log('itemLine',${itemLine})
}
</script>
</body>
</html>`;
scriptContext.response.write(htmlContent);
} catch (error) {
log.error("Error @ salesOrderDetailsPage", error);
}
}
function checkIfExternal(request) {
try {
let host = request.headers['host'];
// Ensure host is defined before proceeding
if (!host) {
throw new Error("Host header is undefined");
}
// NetSuite external URLs usually contain '.extforms.netsuite.com'
return host.includes('.extforms.netsuite.com');
} catch (error) {
log.error("Error in checkIfExternal", error);
return false; // Default to false in case of an error
}
}
/**
* Retrieves the full URL of a file given its path or ID.
*
* @param {string} filePath - The path or ID of the file to load.
* @returns {string} The full URL of the file, or an empty string if an error occurs.Domain(filePath)
*/
function getFileUrl(filePath) {
try {
// Load the file using its path or ID
let fileObj = file.load({
id: filePath
});
// Retrieve the relative URL of the file
let fileRelativeUrl = fileObj.url;
// Use the `N / url` module to get the base domain dynamically
let baseDomain = url.resolveDomain({ hostType: url.HostType.APPLICATION });
// Construct and return the full file URL
return 'https://' + baseDomain + fileRelativeUrl;
} catch (error) {
log.error("Error loading file", error);
return '';
}
}
/**
* Retrieves the relative URL of a file without the domain.
*
* @param {string} filePath - The path or ID of the file to load.
* @returns {string} The relative URL of the file, or an empty string if an error occurs.
*/
function getFileUrlNoDomain(filePath) {
try {
// Load the file using its path or ID
let fileObj = file.load({
id: filePath
});
// Retrieve the relative URL of the file
let fileRelativeUrl = fileObj.url;
return fileRelativeUrl;
} catch (error) {
log.error("Error loading file", error);
return '';
}
}
/**
* Resolves the URL for a given script and deployment.
*
* @param {string} scriptId - The internal ID of the script.
* @param {string} deploymentId - The internal ID of the script deployment.
* @returns {string} The resolved URL for the script deployment.
*/
function getResolvedUrl(scriptId, deploymentId, isExternal) {
try {
return url.resolveScript({
scriptId: scriptId,
deploymentId: deploymentId,
returnExternalUrl: isExternal,
});
} catch (error) {
log.error('error @getResolvedUrl',error)
}
}
function getNewItem(itemInternalId, customerCategory) {
try {
if (!itemInternalId) {
log.error("Error @getNewItem", "Item Internal ID is missing.");
return null;
}
let filter = [
["type", "anyof", "InvtPart"],
"AND", ["internalid", "anyof", itemInternalId]
];
if (customerCategory) {
filter.push("AND", ["pricing.pricelevel", "anyof", 3]);
} else {
filter.push("AND", ["pricing.pricelevel", "anyof", 1]);
}
let inventoryItemSearchObj = search.create({
type: "inventoryitem",
filters: filter,
columns: [
search.createColumn({ name: "internalid", label: "Internal ID" }),
search.createColumn({ name: "itemid", label: "Item Name" }),
search.createColumn({ name: "baseprice", label: "Base Price" }),
search.createColumn({ name: "unitprice", join: "pricing", label: "Unit Price" }),
search.createColumn({ name: "pricelevel", join: "pricing", label: "Price Level" })
]
});
let searchResults = inventoryItemSearchObj.run().getRange({ start: 0, end: 1 });
if (searchResults.length > 0) {
let result = searchResults[0];
return {
id: result.getValue({ name: "internalid" }),
name: result.getValue({ name: "itemid" }),
rate: result.getValue({ name: "unitprice", join: "pricing" }) || result.getValue({ name: "baseprice" }) || 0,
quantity: 1
};
} else {
return null;
}
} catch (error) {
log.error("Error @getNewItem", error);
return null;
}
}
/**
* Defines the Suitelet script trigger point.
* @param {Object} scriptContext
* @param {ServerRequest} scriptContext.request - Incoming request
* @param {ServerResponse} scriptContext.response - Suitelet response
* @since 2015.2
*/
const onRequest = (scriptContext) => {
try {
let isExternal = false;
isExternal = checkIfExternal(scriptContext.request);
log.debug('isExternal', isExternal);
log.debug('script context', scriptContext.request.parameters)
if (scriptContext.request.method === 'GET') {
let backgroundFileUrl = getFileUrl(BACKGROUND_IMAGE_PATH);
let fullFileUrl = getFileUrl(LOGO_FILE_PATH);
let salesOrder = scriptContext.request.parameters.salesOrderId;
let salesOrderdetails = getsalesOrder(salesOrder)
let itemhtmlFileUrl = getFileUrlNoDomain(ITEM_HTML_PATH);
let scanningPageUrl = getResolvedUrl('customscript_jj_sl_so_details_crys_192', 'customdeploy_jj_sl_so_details_crys_192', isExternal);
let itemLine = {}
let params = scriptContext.request.parameters;
if ('PreRecId' in params) {
log.debug('removing PrerecId', params.PreRecId)
delete params.PreRecId;
}
let itemInternalId = scriptContext.request.parameters.itemInternalId;
log.debug('itemInternalId',itemInternalId);
if(itemInternalId)
{
let customerCategory = salesOrderDetailsPage.customerCategory
itemLine = getNewItem(itemInternalId, customerCategory);
itemLine = JSON.stringify(itemLine);
log.debug('itemLine',itemLine)
salesOrderDetailsPage(scriptContext, backgroundFileUrl, fullFileUrl, salesOrderdetails, itemhtmlFileUrl, scanningPageUrl, itemLine);
}
salesOrderDetailsPage(scriptContext, backgroundFileUrl, fullFileUrl, salesOrderdetails, itemhtmlFileUrl, scanningPageUrl, itemLine = null);
}
}
catch (e) {
log.error('Error in onRequest', e);
}
};
return { onRequest }
});
