Pagination in Suitescript 2.0 Netsuite - netsuite

I tried implementing netsuite pagination in suitescript 2.0, the prev button is working but instead of 20 search data it's return like whole. Next button is not working always throwing error INVALID_PAGE_RANGE
Here is the suitelet code
var loadSearch = search.load({
id: 'customsearch_inv123'
});
var i = 0;
var pagedData = loadSearch.runPaged({
pageSize: 20
});
pagedData.pageRanges.forEach(function (pageRange) {
var lastPageRange = pagedData.pageRanges[pagedData.pageRanges.length - 1];
var Page = pagedData.fetch({
index: lastPageRange.index
});
if (context.request.parameters.next) {
Page = Page.next();
}
if (context.request.parameters.prev) {
Page =Page.prev();
}
Page.data.forEach(function (result) {
var number = result.getValue({
name: 'inventorynumber'
});
if (number) {
updateSublist.setSublistValue({
id: 'custpage_number',
line: i,
value: number
});
}
i++;
});
});
// submit button
form.addSubmitButton({
label: 'Submit'
});
form.addButton({
id: '_next',
label: 'Next',
functionName: 'next'
});
form.addButton({
id: '_prev',
label: 'Prev',
functionName: 'prev'
});
form.clientScriptModulePath = 'SuiteScripts/client.js';
In client script i wrote the function next and prev, and redirected to the same page with next or prev parameters and based on that i called page.next and page.prev.
If you have implemented this please help me.
here is client code.
next: function () {
window.location.href = 'example.com' + '&next=t';
},
prev: function () {
window.location.href = 'example.com' + '&prev=t';
}

You should pass current/requested pageNumber from client script and then use it to fetch data for that specific page like below
var pagedData = loadSearch.runPaged({
pageSize: 20
});
requestedPageNumber = 2
var page = pagedData.fetch({ index: requestedPageNumber });
page.data.forEach(function (result) {
// result is the searchResult object
// YOUR CODE GOES HERE
var number = result.getValue({
name: 'inventorynumber'
});
})
What you did wrong was you were starting from the last index and then trying to fetch unexisting array index in case where user clicks next.
Note: previous button should be disabled/hidden where there are no pages so as to not run into the same error and the same could be done for the next button too.

Related

Netsuite Print Button - "error.SuiteScriptError","name":"MISSING_PDF_PARAMETERS",

I have created a print button to print an advanced PDF from a sales order, so the client can get a commercial invoice generated. I have done this for other clients with success but I am running into an issue where the standard templates will print a pdf from the button but the customized ones will not. Where am I going wrong in the code here:
Suitelet:
function onRequest(context) {
var custom_id = context.request.parameters.custom_id;
var tempid = context.request.parameters.tempid;
log.debug("id", custom_id);
log.debug("id", context.request.parameters);
var pdfFileName = "Commercial Invoice";
var renderer = render.create();
var content = renderer.addRecord({
templateName: 'record',
record: record.load({
type: record.Type.SALES_ORDER,
id: custom_id
})
});
renderer.setTemplateByScriptId(tempid);
var pdfFile = renderer.renderAsPdf();
log.debug ("pdf", pdfFile)
context.response.writeFile(renderer.renderAsPdf(),true);
}
return {
onRequest: onRequest
}
});
Client Script:
function onButtonClick() {
var suiteletUrl = url.resolveScript({
scriptId: 'customscript_commercialinv_so_sl',
deploymentId: 'customdeploy_commercialinv_so_sl',
returnExternalUrl: true,
params: {
custom_id: currentRecord.get().id, tempid: "CUSTTMPL_109_4753601_SB1_795"
},
});
window.open(suiteletUrl,"_blank");
}
exports.onButtonClick = onButtonClick;
exports.pageInit = pageInit;
return exports;
});
My user script looks solid. I am getting this error when I direct the Client script to a custom template as referenced above:
{"type":"error.SuiteScriptError","name":"MISSING_PDF_PARAMETERS","message":"Missing parameters required to generate PDF","stack":["renderAsPdf(N/render)","onRequest(/SuiteScripts/MegWebb/cust_SOprinttemplate_SL.js:28)"],"cause":{"type":"internal error","code":"MISSING_PDF_PARAMETERS","details":"Missing parameters required to generate PDF","userEvent":null,"stackTrace":["renderAsPdf(N/render)","onRequest(/SuiteScripts/MegWebb/cust_SOprinttemplate_SL.js:28)"],"notifyOff":false},"id":"","notifyOff":false,"userFacing":false}
Any help would be much appreciated!

Submit button and Client script button conflicting on Suitelet. SS 2.0

i have 2 buttons on my suitelet form, Standard Submit Button and Client Script button. The submit button does not execute if i have the clientScriptModulePath in my suitelet code. once i remove the clientScriptModulePath , the submit button starts working. Anyone have any idea what could be the reason?
Suitelet:
function onRequest(context) {
log.debug('request type', context.request.method);
var form = serverWidget.createForm({ title: 'Item Search' });
if (context.request.method === 'GET') {
var rec_id = context.request.parameters.id;
log.debug('rec id', rec_id)
form.addButton({
id: 'custpage_btn_goback',
label: 'Go Back',
functionName: 'onBackButtonClick'
});
form.clientScriptModulePath = '../client/mx_itemsearch_filter_cs.js';
form.addSubmitButton({
label: 'Submit'
});
context.response.writePage(form);
}
else {
log.debug('POST', 'POST')
}
}
Client script function:
function onBackButtonClick() {//TODO:
try {
console.log('in back button')
var currRec = currentRecord.get();
var suiteletUrl = url.resolveScript({
scriptId: 'customscript_mx_itemsearch_sl',
deploymentId: 'customdeploy_mx_itemsearch_sl'
});
console.log(suiteletUrl)
window.open(suiteletUrl, '_self');
} catch (e) {
log.error({ title: fx, details: 'Error - Code: ' + e.code + ', Message: ' + e.message });
}
}
You need to add one valid client side endpoint function. The endpoint function can just be a blank function. Example
function pageInit (context) {};
The your client script should have export the client endpoint function and your custom function.
return { pageInit: pageInit, onBackButtonClick: onBackButtonClick};

Proper async/await function

I am attempting to run a bot that scrapes Amazon (using amazon-buddy) for certain products (using array of ASINs) and checks the price. If the price is not 0, it should be sending a message on discord. I currently have this set to run every 30 seconds and it's working, but there are times where it seems like each element is not waiting for the previous one to get a response in the forEach loop and my function doesn't seem to be correct (I'm still trying to understand async/await functions properly).
Is there a better way to run this so that each element waits for the previous element to get scraped before moving on to the next one and THEN run the loop again after 30 seconds?
(function() {
var c = 0;
var timeout = setInterval(function() {
const checkStock = (async () => {
config.items.itemGroup.forEach(element => {
console.log('Checking stock on ' + element)
try {
const product_by_asin = await amazonScraper.asin({ asin: element });
console.log(product_by_asin)
const price = product_by_asin.result[0].price.current_price
const symbol = product_by_asin.result[0].price.symbol
const asin = product_by_asin.result[0].asin
const title = product_by_asin.result[0].title
const url = product_by_asin.result[0].url
const image = product_by_asin.result[0].main_image
if (price != 0) {
const inStockResponse = {
color: 0x008000,
title: title + ' is in stock!',
url: url,
author: {
name: config.botName,
icon_url: config.botImg,
url: config.botUrl
},
description: '<#767456705306165298>, click the tite to go purchase!\n\n' +
'Price: ' + symbol + price,
thumbnail: {
url: image
},
timestamp: new Date()
}
message.channel.send({embed: inStockResponse });
console.log(title + ' (' + asin + ') IS available!')
} else {
console.log(title + ' (' + asin + ') IS NOT available!')
}
} catch (error) {
console.log(error);
}
});
checkStock()
});
console.log('Counter: ' + c)
c++;
}, 30000);
})();
You could use a for...of loop which can wait for each iteration to finish:
async function checkItems(items) {
// Check all items, wait for each to complete.
for (const item of items) {
try {
const product_by_asin = await amazonScraper.asin({ asin: item });
console.log(product_by_asin);
const price = product_by_asin.result[0].price.current_price;
const symbol = product_by_asin.result[0].price.symbol;
const asin = product_by_asin.result[0].asin;
const title = product_by_asin.result[0].title;
const url = product_by_asin.result[0].url;
const image = product_by_asin.result[0].main_image;
if (price != 0) {
const inStockResponse = {
color: 0x008000,
title: title + " is in stock!",
url: url,
author: {
name: config.botName,
icon_url: config.botImg,
url: config.botUrl,
},
description:
"<#767456705306165298>, click the tite to go purchase!\n\n" +
"Price: " +
symbol +
price,
thumbnail: {
url: image,
},
timestamp: new Date(),
};
// NOTE: you might want to wait for this too, the error
// currently isn't being handled like this either.
message.channel.send({ embed: inStockResponse });
console.log(title + " (" + asin + ") IS available!");
} else {
console.log(title + " (" + asin + ") IS NOT available!");
}
} catch (err) {
console.log(err);
}
}
// Wait 30s and check again.
setTimeout(() => checkItems(items), 30000);
}
checkItems(config.items.itemGroup);

How to properly order async functions in node.js?

i got 3 adaptive cards that should display correctly one after the other, depending on which button is pushed. Unfortunately they just show randomly till the wished one appears. My function is pretty miserable and consumes too much in testing. Any suggestion is welcomed.
InputCard works fine cos is attached at the beginning in the index.js file, the mess starts later with the three ones exposed here. InputCard is repeated cos of the context.sendActivity('Back to menu').
async processCards (context, luisResult, next) {
this.logger.log('process');
// Retrieve LUIS result for Process Automation.
const result = luisResult.connectedServiceResult;
const intent = result.topScoringIntent.intent;
let InputCard = require('../resources/FaqCard.json');
let InputCard1 = require('../resources/InputCard.json');
let InputCard3 = require('../resources/UserPsw.json');
let CARDS = [
InputCard,
InputCard1,
InputCard3
];
let reply = { type: ActivityTypes.Message };
let buttons = [
{ type: ActionTypes.Submit, title: 'F.A.Q', value: '1' },
{ type: ActionTypes.Submit, title: 'Back to menu', value: '2' },
{ type: ActionTypes.Submit, title: 'login', value: '3' }
];
let card = CardFactory.heroCard('say what???', undefined,
buttons, { text: 'You can upload an image or select one of the following choices.' });
reply.attachments = [card];
let SelectedCard = CARDS[Math.floor((Math.random() * CARDS.length - 1) + 1)];
if (await context.sendActivity(`F.A.Q`))
return context.sendActivity({
attachments: [CardFactory.adaptiveCard(SelectedCard)]
});
if (await context.sendActivity('Back to menu'))
return context.sendActivity({
attachments: [CardFactory.adaptiveCard(SelectedCard) = this.CARDS(InputCard)]
});
if (await context.sendActivity('login'))
return context.sendActivity({
attachments: [CardFactory.adaptiveCard(SelectedCard) = this.CARDS(InputCard3)]
});
await context.sendActivity(`processCards top intent ${intent}.`);
await context.sendActivity(`processCards intents detected: ${luisResult.intents.map((intentObj) => intentObj.intent).join('\n\n')}.`);
if (await context.sendActivity(`myName ${intent}.` == 'myName')) {
return context.sendActivity(`ok, anything else????`);
}
if (luisResult.entities.length > 0) {
await context.sendActivity(`processCards entities were found in the message: ${luisResult.entities.map((entityObj) => entityObj.entity).join('\n\n')}.`);
await context.sendActivity(`ok, anything else?`);
}
await next();
}
After InputCard,should be possible to choose between FaqCard.json or UserPsw.json and after that,if wished, returning to InputCard too.

Creating form in netsuite using suitscript 2.0

var formData = new FormData();
formData.append("name", "John");
formData.append("age", "31");
for (var value of formData.values()) {
log.debug(value);
}
but when i want to log form values using formData api. It's giving below error.
ReferenceError: "FormData" is not defined.
FormData is a client side API managed under XMHttpRequest
UserEvent scripts are server side scripts with no browser based APIs available at all.
So you could use FormData in a client script to send info to a Suitelet or RESTlet but it's not present in a UserEvent script.
If you want to create a form in a Suitelet using SS2.0 you can use the following as a sample:
/**
*#NApiVersion 2.x
*#NScriptType Suitelet
*/
define(["N/log", "N/redirect", "N/runtime", "N/ui/serverWidget", "N/url", "./kotnRECBCFilters"],
function (log, redirect, runtime, ui, url, kotnRECBCFilters_1) {
function showPropertiesForm(context) {
var form = ui.createForm({
title: 'Property Trust Ledger'
});
var req = context.request;
var fromLoc = form.addField({
id: 'custpage_loc',
type: ui.FieldType.SELECT,
label: 'For Property',
source: 'location'
});
fromLoc.updateLayoutType({ layoutType: ui.FieldLayoutType.NORMAL });
fromLoc.updateBreakType({ breakType: ui.FieldBreakType.STARTCOL });
if (req.parameters.custpage_loc) {
fromLoc.defaultValue = req.parameters.custpage_loc;
}
var notAfterDate = form.addField({
id: 'custpage_not_after',
type: ui.FieldType.DATE,
label: 'On or Before'
});
if (req.parameters.custpage_not_after) {
notAfterDate.defaultValue = req.parameters.custpage_not_after;
}
form.addSubmitButton({
label: 'Get Detail'
});
//... bunch of stuff removed
context.response.writePage(form);
}
function onRequest(context) {
if (context.request.method === 'POST') {
var currentScript = runtime.getCurrentScript();
var params = {};
for (var k in context.request.parameters) {
if (k.indexOf('custpage_') == 0 && k.indexOf('custpage_transactions') == -1) {
if ((/^custpage_.*_display$/).test(k))
continue;
params[k] = context.request.parameters[k];
}
}
redirect.toSuitelet({
scriptId: currentScript.id,
deploymentId: currentScript.deploymentId,
parameters: params
});
return;
}
showPropertiesForm(context);
}
exports.onRequest = onRequest;
});

Resources