I am running into the issue below when trying to edit a CUSTOMER record in NetSuite. The script I have created is very simple.
What could I possibly doing wrong with such a simplistic piece of code?
{"type":"error.SuiteScriptModuleLoaderError","name":"MODULE_DOES_NOT_EXIST","message":"Module does not exist: /SuiteScripts/BillingInfoUpdated.js","stack":[]}
SCRIPT:
define(['N/log'], function (log) {
/**
* User Event 2.0 example showing usage of the Submit events
*
* #NApiVersion 2.x
* #NModuleScope SameAccount
* #NScriptType UserEventScript
* #appliedtorecord customer
*/
var exports = {};
function afterSubmit(scriptContext) {
log.debug({
"title": "After Submit",
"details": "action=" + scriptContext.type
});
}
exports.afterSubmit = afterSubmit;
return exports;
});
Add .js to the end of the script file name
Nathan Sutherland answer worked for me and it's absolutely fine But I'm writing this answer so new user get that faster and not confused with other names.
You need to add .js where the blue arrow points.
On starting while creating script.
if you forget then edit here
Use this instead:
var LOGMODULE; //Log module is preloaded, so this is optional
/**
*#NApiVersion 2.x
*#NModuleScope Public
*#NScriptType UserEventScript
*/
define(['N/log'], runUserEvent);
function runUserEvent(log) {
LOGMODULE = log;
var returnObj = {};
returnObj.afterSubmit = afterSubmit;
return returnObj;
}
function afterSubmit(context) {
log.debug('After Submit', "action=" + context.type);
//LOGMODULE.debug('After Submit', "action=" + context.type); //Alternatively
//context.newRecord; //Just showing how to access the records
//context.oldRecord;
//context.type;
return;
}
For more 2.0 Quickstart Samples: ursuscode.com
Related
Im trying to display a confirmation message when a record is correctly saved and an error message when its not.
But when I try to save the script I get
SuiteScript 2.x entry point scripts must implement one script type function
appears and i don't know why. Here is the script:
/**
* #NApiVersion 2.x
* #NScriptType ClientScript
*
*/
define('[N/ui/message]', function(message){
function saveRecord(context){
var currentRecord = context.currentRecord;
var pName = currentRecord.getValue('name');
var pAge = currentRecord.getValue('custrecord_p_age');
var pLastName = currentRecord.getValue('custrecord_plastname');
if(pName != null && pAge != null && pLastName != null ){
var confirmationMsg = message.create({
title: 'Enhorabuena',
message: 'La persona ah sido ingresada con exito',
Type: message.Type.CONFIRMATION
});
confirmationMsg.show({
duration: 5000
});
return true;
}else{
var errorMsg = message.create({
title: 'Enhorabuena',
message: 'La persona ah sido ingresada con exito',
Type: message.Type.ERROR
});
errorMsg.show({
duration: 5000
});
return false;
}
}
return{
saveRecord: saveRecord
}
});
Change
/** *#NApiVersion 2.x *#NScriptType ClientScript * */
to (remove the extra * towards the end of the tag)
/**
*#NApiVersion 2.x
*#NScriptType ClientScript
*/
Also change
define('[N/ui/message]', function(message){
to (move the single quotes to inside the brackets)
define(['N/ui/message'], function(message){
Try a stripped down version in the debugger with "Require." Your code seems fine. Also write it in Visual Code so you can see the formatting. Take out the functionality and see what is missing in structure for suitescript 2.0, example:
/**
* #NApiVersion 2.x
* #NScriptType Restlet
*/
define(["N/log"], function(log) {
function dostuff(context) {
//do stuff
}
return {
post: dostuff
};
});
I have created a button on the work order records that opens a suitelet form with a fieldType.FILE to allow the user to choose a file locally. When the Suitlet is submitted I want the script to take the file, take the contents, and then pass two arrays to a scheduled script. So far I have no errors and none of my log.debugs are showing up in Netsuite.
Here is the suitelet code for reference:
/**
* #NApiVersion 2.x
* #NScriptType Suitelet
*
* #author MF
*/
define(['N/ui/serverWidget', 'N/file', 'N/search', 'N/email','N/ui/dialog'], function (serverWidget, file, search, email, dialog) {
function onRequest(context) {
//getWoData(context);
buildPage(context);
};
function buildPage(context) {
var woForm = serverWidget.createForm({
title: "Mass Update Work Order Due Dates",
})
woForm.addField({
id: 'custpage_wo_export',
label: "Work Order Update File",
type: serverWidget.FieldType.FILE
});
woForm.addSubmitButton({
id: 'custpage_submitwo',
label: 'Submit',
functionName: 'SubmitWorkOrders'
});
woForm.clientScriptModulePath = "SuiteScripts/WoSubmitClient.js"
context.response.writePage(woForm);
}
return {
onRequest: onRequest
};
});
The issue starts at the client as far as I know.
Here is the client script for the Suitelet above:
/**
* #NApiVersion 2.X
* #NScriptType ClientScript
* #NModuleScope SameAccount
* #author MF
*/
define(['N/url', 'N/https','N/currentRecord'], function (url, https, currentRecord) {
function pageInit(context) { };
function SubmitWorkOrders(context) {
var objRecord = currentRecord.get();
//Handle the save button
log.debug({
title: 'SubmitWorkOrders',
details: "This function was called"
})
var uploadedFile = objRecord.getValue({
fieldId: 'custpage_wo_export'
})
log.debug({
title: 'File',
details: uploadedFile.name
})
var taskCreate = url.resolveScript({
scriptId: 'customscript_scheduledscripttaskagent',
deploymentId: 'customdeploy_scheduledscripttaskagent',
returnExternalUrl: false,
params: {
input_file: uploadedFile
}
});
}
return {
pageInit: pageInit,
saveRecord: SubmitWorkOrders
};
});
This client script then calls another suitelet to create and run the task.
/**
* #NApiVersion 2.x
* #NScriptType Suitelet
*
* #author MF
*/
define([
'N/task',
'N/redirect',
'N/file'
], function (task, redirect, file) {
function scriptTask(context) {
//check file type
var input_file = context.request.params.input_file;
log.debug({
title: 'File Name',
details: input_file.name
})
var woUpdateScriptTask = task.create({
taskType: task.taskType.SCHEDULED_SCRIPT,
deploymentId: 'customscript_womassupdate',
params: { uploadedFile: uploadedFile },
scriptId: 715
}).submit();
}
return {
onRequest: scriptTask
};
});
Any advice would be appreciated, I have only been in the Netsuite realm for a few weeks and javascript in general only a bit longer than that. Note that there is some residual modules in some of these scripts from testing different approaches.
Currently the entire process looks something like this -> (1)UserEventScript on Work Orders -> (2)ClientScript on Work Orders -> (3)First attached Suitelet -> (4)ClientScript for Suitelet -> (5)Suitelet to create the task to run the scheduled script -> (6)ScheduledScript to update work orders. Looking to get some advice on getting the file contents from step 3 to step 5 so the data could get parsed before they are passed to the scheduled script.
Thanks in advance
I think this is how you can get the file contents from your first Suitelet to your Scheduled script:
First, you need to combine your 1st and 2nd Suitelet into one single Suitelet. This is required for getting selected file.
You can simply do it like this in your first Suitelet > function onRequest():
function onRequest(context) {
if (request.method == 'GET') { //This block will execute when you open your Suitelet form
buildPage(context);
}else{ //This block will execute when Submit button is clicked
scriptTask(context); // add scriptTask() function body in your 1st Suitelet from 2nd Suitelet
}
};
Second, if your client script is attached with a Suitelet you can view it's logs using console.log instead of log.debug in web browser's console (open with Ctrl+Shift+i). BUT getting value of 'custpage_wo_export' in your CS will only return a string like 'C:\fakepath\your_file_name.file_extension'. You might need to first save selected file in NetSuite's File Cabinet to access it's contents. This can be done in function scriptTask() now in your 1st Suitelet.
In Client Script > function SubmitWorkOrders(), use:
function SubmitWorkOrders(context) {
var objRecord = context.currentRecord;
var uploadedFile = objRecord.getValue({
fieldId: 'custpage_wo_export'
})
console.log('File', uploadedFile);
return true; //use return 'false' to view the log in console first
}
Now Third, in function scriptTask(), now in 1st Suitelet, use:
function scriptTask(context) {
if (context.request.files.custpage_wo_export) {
var fileObj = context.request.files.custpage_wo_export; //save this fileObj and then access it's contents.
log.debug('fileObj', fileObj);
fileObj.folder = 1630; //speficy the id of folder in File Cabinet
var fileId = fileObj.save();
log.debug("fileId", fileId);
//Now, load newly saved file and access it's contents. You can also access file contents in your Scheduled script depending on what you prefer/require.
if (fileId) {
var fileObj = file.load({
id: fileId
});
fileContents = fileObj.getContents();
log.debug("fileContents", fileContents);
/*
Now, you have fileId and/or fileContents. You can execute your Scheduled script here using task.create() etc.
*/
}
}
}
Note: Selecting and uploading/saving the same file in File Cabinet will simply overwrite the existing file keeping file id same.
I am using Suitescript 2.0. There I am trying to reschedule a script for a particular type of error.
I got the below code which can be used to rescheduled the script immediately.
var scriptTask = task.create({
taskType: task.TaskType.MAP_REDUCE
});
scriptTask.scriptId = 'customscript_id';
scriptTask.deploymentId = 'customdeploy_id';
var scriptTaskId = scriptTask.submit();
But I am mainly looking for some option to run it after a certain time like after an hour.
Is it possible to achieve by passing any kind of parameter to the above task?
Any other alternative approach would also helpful.
I had a similar issue, I needed to delay my schedule a certain time in your case a map/reduce script which should be the same.
I fixed it with this approach.
Here is sample code with the approach.
/**
#NApiVersion 2.x
#NScriptType ScheduledScript
#NModuleScope SameAccount
*/
define(['N/file', 'N/record', 'N/render', 'N/runtime', 'N/search', 'N/ui/serverWidget', 'N/format', 'N/task', 'N/log'],
function(file, record, render, runtime, search, serverWidget, format, task, log) {
/**
* Definition of the Scheduled script trigger point.
*
* #param {Object} scriptContext
* #param {string} scriptContext.type - The context in which the script is executed. It is one of the values from the scriptContext.InvocationType enum.
* #Since 2015.2
*/
function execute(scriptContext) {
wait(20000); // it waits 20 sec
//whatever you want to do
}
function wait(ms){
var start = new Date().getTime();
var end = start;
while(end < start + ms) {
end = new Date().getTime();
}
}
return {
execute: execute
};
});
I have built a script that looks at fields within a saved search to process, when the script processes each line of the saved search . The script has to process a lot of lines of data and I'm running into issue where I get an SSS Usage Limit exceeded message just due to the nature of the amount of information that I'm processing. Therefore I'm wondering if on each run of the script if I can limit the amount of orders processed and then consecutively run the script until there aren't any more records that need to be processed
Previously I have just restricted the number of records that the script processes at a time and then just manually trigger the script until there aren't anymore lines left to process. I see that you can trigger the script to run every 15 minutes or so but would like it to run each day and then run in a 15 minute cadence after that until the saved search has been exhausted. Then the script would be triggered to run again the following day etc for every 15 min until all records are processed. From research don't know if this type of scheduling is possible.
code itself is working the scheduling of it is what I need guidance on
The other alternative (and one that I've used successfully) is to exit the scheduled script whent the usage reaches a certain point, but before you exit, trigger the scheduled script again. I've used this successfully to process thousands of records (such as mass deletions).
/**
* #NApiVersion 2.x
* #NScriptType ScheduledScript
* #NModuleScope SameAccount
*/
define(['N/record', 'N/runtime', 'N/search', 'N/task'],
/**
* #param {record} record
* #param {search} search
*/
function(record, runtime, search, task) {
const governanceCap = 9950;
function getAllResults(s) {
var results = s.run();
var searchResults = [];
var searchid = 0;
do {
var resultslice = results.getRange({start:searchid,end:searchid+1000});
resultslice.forEach(function(slice) {
searchResults.push(slice);
searchid++;
}
);
} while (resultslice.length >=1000);
return searchResults;
}
/**
* Definition of the Scheduled script trigger point.
*
* #param {Object} scriptContext
* #param {string} scriptContext.type - The context in which the script is executed. It is one of the values from the scriptContext.InvocationType enum.
* #Since 2015.2
*/
function execute(scriptContext) {
function rescheduleCurrentScript() {
var scheduledScriptTask = task.create({
taskType: task.TaskType.SCHEDULED_SCRIPT
});
scheduledScriptTask.scriptId = runtime.getCurrentScript().id;
scheduledScriptTask.deploymentId = runtime.getCurrentScript().deploymentId;
return scheduledScriptTask.submit();
}
try {
var script = runtime.getCurrentScript();
// GET YOUR SEARCH HERE
var mySearch = getAllResults(
search.create({
type: "transaction",
filters:
[
["mainline","is","T"],
],
columns:
[
"name",
"tranid",
"type",
search.createColumn({
name:"datecreated",
sort: search.Sort.DESC
}),
]
})
);
var recCount = mySearch.length;
for (each in mySearch) {
try {
record.delete({
type: transSearch[each].getValue({name:'type'}),
id: transSearch[each].id
});
} catch (err) {log.error(err.name,err.message) }
var govPointsUsed = 10000-script.getRemainingUsage();
script.percentComplete = (govPointsUsed/governanceCap*100).toFixed(1);
if (govPointsUsed >= governanceCap) {
var taskId = rescheduleCurrentScript();
log.audit('Rescheduling status: ','Task ID:' + taskId);
return;
}
}
} catch (err) { log.error(err.name,err.message + '; Stack: '+err.stack ) };
}
return {
execute: execute
};
});
Worked like a charm!!
I would encourage you to use a Map/Reduce script. That is the correct script type to use when dealing with a lot of data and can handle governance issues much better.
I had this problem some time ago. The map/reduce script can be the solution if you run 2.0 scripts. In my case, I had 'legacy' scripts in versions 1.X and 2.X. And all of them throw from time to time 'SSS_TIME_LIMIT_EXCEEDED'.
My solution :)
I created a script, kind of 'listener'. I removed link to the script because the rep. is not active anymore
// *add the script listener
// [it can be used for both script versions 1.X and 2.X]
/** you have to calculate the 'process usage' by 1 loop cycle.
use script.getRemainingUsage() */
var recallValue = 200;
var stopValue = 200;
var recallIsRequired = true;// use false to stop the script without recall
var script = setScriptListener(recallValue, stopValue, recallIsRequired);
for (var data in data_contaniner){
if(script.canContinue()){
//run you processing
// !important : mark the data as 'processed'.
//otherwise you will have a lot of duplicates after script recall
}else{
//stop the loop
break;
}
}
I am working on a fairly large project coming up, and I am (slowly) finding the ins and outs with requireJS. I can say I love it (the concept) and doubt I'll ever go back to "the old way." That said, I'd like to develop good habitats from the start.
I have a main module called application.js
'use strict';
/**
* Primary application module.
*
* #param {function} jquery | jQuery library. http://jquery.com/
* #returns {object}
*
*/
define([
'jquery'
], function (jquery) {
var Application = {
/**
* Initialization method.
*
* #returns {void}
*
*/
initalize: function () {
// Start doing stuff...
}
};
return Application;
});
So far so good.
What I' curious about, is when I start adding more and more modules. Let's say at some point I have a "contact us" form or some other module that doesn't necessarily need loaded at start up, but will need to be available on page "x".
Would I load it in the list of initial dependencies like this:
'use strict';
/**
* Primary application module.
*
* #param {function} jquery | jQuery library. http://jquery.com/
* #param {object} moduleone | ModuleOne
* #returns {object}
*
*/
define([
'jquery'
'moduleone'
], function (jquery, ModuleOne) {
var Application = {
/**
* Initialization method.
*
* #returns {void}
*
*/
initalize: function () {
// Start doing stuff...
if ($('#moduleOneThing').length) {
// Do stuff with ModuleOne...
}
}
};
return Application;
});
Or is it better to set another define/require block like this:
'use strict';
/**
* Primary application module.
*
* #param {function} jquery | jQuery library. http://jquery.com/
* #param {object} moduleone | ModuleOne
* #returns {object}
*
*/
define([
'jquery'
], function (jquery) {
var Application = {
/**
* Initialization method.
*
* #returns {void}
*
*/
initalize: function () {
// Start doing stuff...
if ($('#moduleOneThing').length) {
require(['moduleone'], function (ModuleOne) {
// Do stuff with Module One...
});
}
}
};
return Application;
});
Or, perhaps I'm way off on everything. In any case I would like to learn the "correct" way from the start in hopes to not have to break bad habits down the road. This is an extremely simple example, but the complexity will grow quickly and I am hopeful to set things up correctly to avoid a pile of steaming garbage in the next few weeks.
Thank you for your time!