How do I save and submit form data in netsuite? - netsuite

I need to save and submit the information for a form for enrollment > employees, each time it is filled out. I need to save and submit the information for a form for enrollment > employees each time it is filled out. I sent an image of where I want the information to be stored. I created a send button, but it doesn’t send
ImageExample
/**
*#NApiVersion 2.x
*#NScriptType Suitelet
*/
define(['N/ui/serverWidget'], function (serverWidget) {
function onRequest(context) {
var formulario = serverWidget.createForm({
title: 'Formulário',
hideNavBar: false
});
formulario.addButton({
id: 'buttonid',
label: 'Cancelar'
})
formulario.addSubmitButton({
label: 'Enviar'
});
/*
formulario.addField({
id: 'field_test',
type: serverWidget.FieldType.INTEGER,
label: 'campo teste',
container: 'fieldgroupcapture'
})
*/
formulario.addField({
id: 'sublista_campo1',
label: 'Código empresa',
type: serverWidget.FieldType.INTEGER
});
formulario.addField({
id: 'sublista_campo2',
label: 'Código funcionário',
type: serverWidget.FieldType.TEXT
})
formulario.maxLength = 5;
formulario.addField({
id: 'sublista_campo3',
label: 'Código evento',
type: serverWidget.FieldType.INTEGER
});
var fieldReference = formulario.addField({
id: 'sublista_campo4',
label: 'Referência',
type: serverWidget.FieldType.INTEGER
});
var fieldValue = formulario.addField({
id: 'sublista_campo5',
label: 'Valor',
type: serverWidget.FieldType.FLOAT
});
formulario.addField({
id: 'sublista_campo6',
label: 'Mês e ano',
type: serverWidget.FieldType.DATE
});
if (fieldReference === '' && fieldReference === null) {
fieldReference.defaultValue = '0000'
};
if (fieldValue === '' && fieldValue === null) {
fieldValue.defaultValue = '0000'
}
context.response.writePage(formulario);
};
return {
onRequest: onRequest
};
});

Your script would need to handle a POST request. Right now it doesn't distinguish between GET and POST so it just shows the form over and over.
function onRequest(context){
if (context.request.method === 'GET') {
... your current code
}else{ // assumes POST
handle creating/updating an employee record
}
}
Another approach to consider would be to just have a heavily edited standard employee form linked to a role. That is then code free except possibly a UserEvent script that forces the edited form to be shown based on user and context.

Related

NetSuite - How to loop thru context newRecord objects when using User Event Script

When utilizing a User Event Script, I am attempting to loop through a context object to find all newRecord objects that are selected by a user in the Inventory Print screen. I am able to retrieve a single record within the context object, however, if a user selects multiple invoices to print, I am unable to retrieve the additional invoices.
When looking at the Execution log, I am unable to see the entire context object.
My script is as follows:
/** #NApiVersion 2.0 #NScriptType UserEventScript
*/
define(['N/record'], function (record) { function
beforeLoad(context) {
try {
var arrInvoiceIds= []
var arrFileNames= []
var r = context.newRecord
var nrf = r.getFields()
//fetch invoice record
var invoiceRecord = record.load({ type: record.Type.INVOICE, id: r.id })
for(var i=0; i < nrf.length; i++){
var customerName = ""
var invNo = ""
var fileName = ""
if(nrf[i] == "entity"){
customerName = invoiceRecord.getValue('entity');
}
if(nrf[i] == "tranid"){
invNo = invoiceRecord.getValue('tranid');
}
if(customerName != "" && invNo != ""){
fileName = customerName + "_" + invNo;
}
}
log.debug ({
title: 'r.id',
details: r.id //arrInvoiceIds //arrFileNames
});
log.debug ({
title: 'nrf',
details: nrf //arrInvoiceIds //arrFileNames
});
log.debug ({
title: 'nrf.length',
details: nrf.length //arrInvoiceIds //arrFileNames
});
log.debug ({
title: 'context',
details: context //arrInvoiceIds //arrFileNames
});
log.debug ({
title: 'file',
details: fileName //arrInvoiceIds //arrFileNames
});
} catch (e) {
log.error ({
title: e.name,
details: e.message
});
}
}
return {
beforeLoad: beforeLoad
}; });
When you print multiple invoices, the UserEvent is simply triggered multiple times. You should see that in your logs. The invoices are not combined on a single instance of context within a single UserEvent.

Can we update a Netsuite "Custom Record type" field by clicking a button?

I have a Suitelet script to create a form, there I pull the information contained in a "Custom Record type" as the default value for the form fields.
Also create a ClientScript for the Suitelet form with the functions that are executed by clicking a button.
The function works, but I don't know how to update the information of the custom record type.
Here is part of my code:
Suitelet:
/**
*#NApiVersion 2.0
*#NScriptType Suitelet
*/
define(['N/record', 'N/search', 'N/ui/serverWidget'],
function(record, search, serverWidget) {
function onRequest(context) {
try {
if (context.request.method === 'GET') {
var form = serverWidget.createForm({
title: 'SKU Information'
});
form.clientScriptFileId = 3060;
//create fields groups to organize the fields
var itemgroup = form.addFieldGroup({
id : 'itemgroup',
label : 'Next item SKU Number'
});
var usegroup = form.addFieldGroup({
id : 'usegroup',
label : 'Who is using this?'
});
//Add the fields
var skufield = form.addField({
id: 'skufield',
type: serverWidget.FieldType.TEXT,
label: 'SKU Number',
container: 'itemgroup'
});
skufield.isMandatory = true;
// create a field with the user using this numbers when clicking the button
var whousingskunumber = form.addField({
id: 'namefield',
type: serverWidget.FieldType.TEXT,
label: 'User:',
container: 'usegroup'
});
whousingskunumber.updateDisplayType({
displayType: serverWidget.FieldDisplayType.DISABLED
});
var usingsince = form.addField({
id: 'sincefield',
type: serverWidget.FieldType.TEXT,
label: 'Using since',
container: 'usegroup'
});
usingsince.updateDisplayType({
displayType: serverWidget.FieldDisplayType.DISABLED
});
// Add the buttons
form.addSubmitButton({
label: 'Update Number'
});
form.addResetButton({
label: 'Cancel'
});
var useNumber = form.addButton({
label: 'Use this number',
id: 'useNumber',
functionName: 'useNumberFunction',
});
var releaseNumber = form.addButton({
label: 'Release usage',
id: 'relaseNumber',
functionName: 'relaseNumberFunction',
});
context.response.writePage(form);
} else {
// Section Four - Output - Used in all sections
var delimiter = /\u0001/;
var skuField = context.request.parameters.skufield;
var whoField = context.request.parameters.whofield;
var ccField = context.request.parameters.cctypefield;
context.response.write('You have entered:'
+ '<br/> New SKU Number: '+ whoField + skuField);
}
var skuNumObj = search.lookupFields({
type: 'customrecordere_lw_lastskunum',
id: 2,
columns: ['custrecord_ere_lw_usingsince', 'custrecordere_lw_nextskunum', 'custrecord_ere_lw_whousingskunum']
});
var usingSince = skuNumObj.custrecord_ere_lw_usingsince;
var whoIsUsing = skuNumObj.custrecord_ere_lw_whousingskunum;
var nextSku = skuNumObj.custrecordere_lw_nextskunum;
skufield.defaultValue = nextSku;
whousingskunumber.defaultValue = 'Nobody';
usingsince.defaultValue = 'You can use it!';
} catch (error) {
log.error({title: 'Failed to get items to update', details: error});
}
}
return {
onRequest: onRequest
};
});
ClientScript
/**
* #NApiVersion 2.x
* #NScriptType ClientScript
*/
//Noslen Pena
define(['N/runtime', 'N/currentRecord', 'N/ui/dialog', 'N/search'],
function (runtime, currentRecord, dialog, search) {
function pageInit(context){
try {
} catch (error) {
log.error({title: 'Failed initializing', details: error});
}
}
function useNumberFunction(){
try {
var skuNumObj = search.lookupFields({
type: 'customrecordere_lw_lastskunum',
id: 2,
columns: ['custrecord_ere_lw_usingsince', 'custrecordere_lw_nextskunum', 'custrecord_ere_lw_whousingskunum']
});
var nextSku = skuNumObj.custrecordere_lw_nextskunum[0].value;
skuNumObj.custrecordere_lw_nextskunum[0].value = Number(nextSku)+1;
dialog.alert({
title: "Information",
message: 'Remember to unlock after you have used'
});
context.skufield.updateDisplayType({
displayType: serverWidget.FieldDisplayType.DISABLED
});
} catch (error) {
log.error({title: 'Failed clicking the button', details: error});
}
}
function relaseNumberFunction(){
dialog.alert({
title: "Thank you!",
message: "Remember to update the number and save the changes"
});
record.namefield.setValue('Nobody');
log.debug({title: 'liberaron el uso', details:'Nobody is using the number now'})
}
return {
pageInit : pageInit,
useNumberFunction : useNumberFunction,
relaseNumberFunction : relaseNumberFunction
};
});
I figured it out, The way to save the changes or submit a new value to the custom record type is like this:
var id = record.submitFields({
type: 'customrecordere_lw_lastskunum',
id: 2,
values: {
custrecord_ere_lw_usingsince : since,
custrecord_ere_lw_whousingskunum : username
},
options: {
enableSourcing: false,
ignoreMandatoryFields : true
}
});

Microsoft Bot Framework (v4): How to make hero card list as prompt choice

I'm trying to create a prompt dialog with hero card list as the choices.
I've created a function that will return the herocard list and use it as the dialog prompt choice.
How can i achieved this? or there is a better way to implement it.
Note: I need to put it in the dialog prompt because I need to implement a sequential conversation. I also put the herocard list in a separate function because I will use it in other dialog prompt.
async selectProduct(stepContext){
return await stepContext.prompt(CHOICE_PROMPT, {
prompt: 'Select Product:',
choices: this.productChoices()
});
}
productChoices(){
const productSeriesOptions = [
CardFactory.heroCard(
'Title 1',
CardFactory.images(['image URL 1']),
CardFactory.actions([
{
type: ActionTypes.ImBack,
title: 'Title 1',
value: 'Value 1'
}
])
),
CardFactory.heroCard(
'Title 2',
CardFactory.images(['image URL 2']),
CardFactory.actions([
{
type: ActionTypes.ImBack,
title: 'Title 2',
value: 'Value 2'
}
])
)
];
return productSeriesOptions;
}
I've included a sample Dialog that demonstrates presenting a carousel of HeroCards to the user (below). The HeroCard has a single button that when clicked results in the next Waterfall step being run.
I originally pulled this dialog from the 'using-cards' sample. So if you wanted to give it a run, you could replace the mainDialog.js in that project and run it in the emulator.
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
const { ActionTypes, AttachmentLayoutTypes, CardFactory } = require('botbuilder');
const { ChoicePrompt, ComponentDialog, DialogSet, DialogTurnStatus, WaterfallDialog, ChoiceFactory } = require('botbuilder-dialogs');
const MAIN_WATERFALL_DIALOG = 'mainWaterfallDialog';
class MainDialog extends ComponentDialog {
constructor() {
super('MainDialog');
// Define the main dialog and its related components.
this.addDialog(new WaterfallDialog(MAIN_WATERFALL_DIALOG, [
this.showProductChoicesStep.bind(this),
this.showCardSelectionStep.bind(this)
]));
// The initial child Dialog to run.
this.initialDialogId = MAIN_WATERFALL_DIALOG;
}
/**
* The run method handles the incoming activity (in the form of a TurnContext) and passes it through the dialog system.
* If no dialog is active, it will start the default dialog.
* #param {*} turnContext
* #param {*} accessor
*/
async run(turnContext, accessor) {
const dialogSet = new DialogSet(accessor);
dialogSet.add(this);
const dialogContext = await dialogSet.createContext(turnContext);
const results = await dialogContext.continueDialog();
if (results.status === DialogTurnStatus.empty) {
await dialogContext.beginDialog(this.id);
}
}
/**
* Send a carousel of HeroCards for the user to pick from.
* #param {WaterfallStepContext} stepContext
*/
async showProductChoicesStep(stepContext) {
console.log('MainDialog.showProductChoicesStep');
await stepContext.context.sendActivity({
attachments: this.productChoices(),
attachmentLayout: AttachmentLayoutTypes.Carousel
});
return { status: DialogTurnStatus.waiting };
}
async showCardSelectionStep(stepContext) {
console.log('MainDialog.showCardSelectionStep');
await stepContext.context.sendActivity('You picked ' + stepContext.context.activity.value);
// Give the user instructions about what to do next
await stepContext.context.sendActivity('Type anything to see another card.');
return await stepContext.endDialog();
}
// ======================================
// Helper functions used to create cards.
// ======================================
productChoices(){
const productSeriesOptions = [
CardFactory.heroCard(
'Product 1',
CardFactory.images(['https://sec.ch9.ms/ch9/7ff5/e07cfef0-aa3b-40bb-9baa-7c9ef8ff7ff5/buildreactionbotframework_960.jpg']),
CardFactory.actions([
{
type: 'messageBack',
title: 'Pick Me',
value: 'product1'
}
])
),
CardFactory.heroCard(
'Product 2',
CardFactory.images(['https://sec.ch9.ms/ch9/7ff5/e07cfef0-aa3b-40bb-9baa-7c9ef8ff7ff5/buildreactionbotframework_960.jpg']),
CardFactory.actions([
{
type: 'messageBack',
title: 'Pick Me',
value: 'product2'
}
])
),
CardFactory.heroCard(
'Product 3',
CardFactory.images(['https://sec.ch9.ms/ch9/7ff5/e07cfef0-aa3b-40bb-9baa-7c9ef8ff7ff5/buildreactionbotframework_960.jpg']),
CardFactory.actions([
{
type: 'messageBack',
title: 'Pick Me',
value: 'product3'
}
])
),
CardFactory.heroCard(
'Product 4',
CardFactory.images(['https://sec.ch9.ms/ch9/7ff5/e07cfef0-aa3b-40bb-9baa-7c9ef8ff7ff5/buildreactionbotframework_960.jpg']),
CardFactory.actions([
{
type: 'messageBack',
title: 'Pick Me',
value: 'product4'
}
])
)
];
return productSeriesOptions;
}
}
module.exports.MainDialog = MainDialog;

Load defaultaddress of customer in netsuite via record search

I am working on creating a RESTlet to load paginated customer records. One of the thing i need is the customer's defaultaddress. This field is available when I load a single customer like this:
customer = record.load({
type: record.Type.CUSTOMER,
id: context.id, // e.g. 1234
isDynamic: false
});
However, when I try to load all customers with pagination like this:
define(['N/record', 'N/search'], function(record, search) {
var currencies = {};
function getClients(context) {
var mySearch = search.create({
type: search.Type.CUSTOMER,
columns: [
{ name: 'companyname' },
{ name: 'vatregnumber' },
{ name: 'lastmodifieddate' },
{ name: 'currency' },
{ name: 'email' },
{ name: 'phone' },
{ name: 'defaultaddress' }
]
});
var searchResult = mySearch.run().getRange({
start: context.start || 0,
end: context.end || 100
});
var results = [];
for (var i = 0; i < searchResult.length; i++) {
results.push({
tax_number: searchResult[i].getValue({ name: 'vatregnumber' }),
name: searchResult[i].getValue({ name: 'companyname' }),
// snipped...
});
}
return results;
}
function loadCurrencyName(id) {
return record.load({
type: record.Type.CURRENCY,
id: id,
isDynamic: false
}).name;
}
return {
get: getClients
}
});
and execute the rest api call to the above RESETlet script; I get the following error:
{
"type":"error.SuiteScriptError",
"name":"SSS_INVALID_SRCH_COL",
"message":"An nlobjSearchColumn contains an invalid column, or is not in proper syntax: defaultaddress.",
"stack":[ ... snipped ... ],
"cause":{ ... snipped ... },
"id":"",
"notifyOff":false
}
Any idea how to best to load the default address of the customer, whilst loading all customers using the paged search feature?
{defaultaddress} is calculated when the customer record is loaded - it's not a stored field and is not available for saved searches. As the field help says, it's simply the default billing address and changes automatically according to what address in the address subtab you have checked 'Default Billing" on.
To work around this and display what you're looking for, you can simply replace defaultaddress with billaddress.

Generate GUID at runtime without using form in NetSuite

Hye there,
I'm trying to prepare a scheduled script in NetSuite which will pick a particular directory from file cabinet and deploy it on SFTP server. I'm using 2.0 module and here is my code -
require(["N/sftp",'N/record','N/file'], function(sftp,record,file) {
function onRequest() {
var myPwdGuid = "13139ac567b14f74bdaXXXXXXX";
var myHostKey = "AAAAB3NzaC1ycXXXXX";
var connection = sftp.createConnection({
username: 'Your user name',
passwordGuid: myPwdGuid,
url: 'Your host name',
directory: '/directory to upload files/',
hostKey: myHostKey
});
var myFileToUpload = file.create({
name: 'originalname.js',
fileType: file.fileType.PLAINTEXT,
contents: 'I am a test file. Hear me roar.'
});
connection.upload({
directory: 'relative/path/to/remote/dir',
filename: 'newFileNameOnServer.js',
file: myFileToUpload,
replaceExisting: true
});
var downloadedFile = connection.download({
directory: 'relative/path/to/file',
filename: 'downloadMe.js'
});
}
onRequest();
return {
onRequest: onRequest
};
});
Now the issue is when i try to run these lines of code i get an error saying "AN_ERROR_OCCURRED_WHILE_DECRYPT_PASSWORDGUID".
What i've found so far through my research is GUID can only be generated by SuitLet form having credential field which will again require GET and POST method. However i Dont want to create a suitelet and invoke it manually in order to generate GUID. All i want to to do is - Run a scheduled script which will Establish connection to SFTP. Pick a directory in file cabinet and upload it on SFTP.
Any help would be greatly appreciated! Thanks in advance!
Its easier and faster than you might think. Take the below code and load it to NetSuite. Create a script file and deployment quick, run the SUITElet to get your GUID, paste that value into your Scheduled Script and don't mess with it again unless the password changes.
/**
*#NApiVersion 2.x
*#NScriptType Suitelet
*/
define([
'N/ui/serverWidget',
'N/https'
],
function (
ui,
https
) {
var HOST_KEY_TOOL_URL = 'https://ursuscode.com/tools/sshkeyscan.php?url=';
function getFormTemplate() {
var form;
form = ui.createForm({
title: 'Password Form'
});
form.addSubmitButton({
label: 'Submit'
});
return form;
}
function addSelectorFields(form) {
var select = form.addField({
id: 'selectaction',
type: ui.FieldType.SELECT,
label: 'Select Action'
});
select.addSelectOption({
value: 'getpasswordguid',
text: 'Get Password GUID'
});
select.addSelectOption({
value: 'gethostkey',
text: 'Get Host Key'
});
return form;
}
function addPasswordGUID1Fields(form) {
var frm = form;
frm.addField({
id: 'restricttoscriptids',
type: ui.FieldType.TEXT,
label: 'Restrict To Script Ids'
}).isMandatory = true;
frm.addField({
id: 'restricttodomains',
type: ui.FieldType.TEXT,
label: 'Restrict To Domains'
}).isMandatory = true;
return frm;
}
function addPasswordGUID2Fields(form, restrictToScriptIds, restrictToDomains) {
form.addCredentialField({
id: 'password',
label: 'Password',
restrictToScriptIds: restrictToScriptIds.replace(' ', '').split(','),
restrictToDomains: restrictToDomains.replace(' ', '').split(',')
});
return form;
}
function addHostKeyFields(form) {
form.addField({
id: 'url',
type: ui.FieldType.TEXT,
label: 'URL (Required)'
});
form.addField({
id: 'port',
type: ui.FieldType.INTEGER,
label: 'Port (Optional)'
});
form.addField({
id: 'hostkeytype',
type: ui.FieldType.TEXT,
label: 'Type (Optional)'
});
return form;
}
function onRequest(option) {
var method;
var form;
var selectAction;
var port;
var hostKeyType;
var restricttoscriptids;
var restricttodomains;
var password;
var theResponse;
var myUrl;
var url;
method = option.request.method;
form = getFormTemplate(method);
if (method === 'GET') {
form = addSelectorFields(form);
}
if (method === 'POST') {
selectAction = option.request.parameters.selectaction;
if (selectAction === 'getpasswordguid') {
form = addPasswordGUID1Fields(form);
} else if (selectAction === 'gethostkey') {
form = addHostKeyFields(form);
} else {
password = option.request.parameters.password;
url = option.request.parameters.url;
port = option.request.parameters.port;
hostKeyType = option.request.parameters.hostkeytype;
restricttoscriptids = option.request.parameters.restricttoscriptids;
restricttodomains = option.request.parameters.restricttodomains;
if (restricttoscriptids && restricttodomains) {
form = addPasswordGUID2Fields(form, restricttoscriptids, restricttodomains);
}
if (password) {
form.addField({
id: 'passwordguidresponse',
type: ui.FieldType.LONGTEXT,
label: 'PasswordGUID Response',
displayType: ui.FieldDisplayType.INLINE
}).defaultValue = password;
}
if (url) {
myUrl = HOST_KEY_TOOL_URL + url + '&port=' + port + '&type=' + hostKeyType;
theResponse = https.get({ url: myUrl }).body;
form.addField({
id: 'hostkeyresponse',
type: ui.FieldType.LONGTEXT,
label: 'Host Key Response',
displayType: ui.FieldDisplayType.INLINE
}).defaultValue = theResponse;
}
}
}
option.response.writePage(form);
}
return {
onRequest: onRequest
};
});
The ability to directly hard-code an SFTP password is not supported in NetSuite. NetSuite uses password tokenization in order to prevent scripts from having access to user credentials. For this reason, only an authenticated user may store a password, and a script may only access it via an identifier (GUID/Token).

Resources