So I am trying to add a button to the Sales Order form in netsuite that validates certain fields based on what you entered in previous fields. I am having trouble testing and debugging this in google chrome in netsuite. First, here is my code: I am adding the button that calls this function within the client script record.
function vsoeValidate(){
var calc = nlapiGetFieldValue('custbody_cv_vsoe_calculation');
calc = nlapiGetFieldValue('custbody_cv_renewal_rev_amount') - (nlapiGetFieldValue('custbody_cv_vsoe_cola') * nlapiGetFieldValue(1-'custbody_cv_vsoe_partner_disc')) - (nlapiGetFieldValue('custbody_cv_vsoe_bts_fees') * (1-nlapiGetFieldValue('custbody_cv_vsoe_partner_disc'))) /
(nlapiGetFieldValue('custbody_cv_vsoe_software_amt') * (1- nlapiGetFieldValue('custbody_cv_vsoe_multiyear_disc')));
nlapiSetFieldValue('custbody_cv_vsoe_calculation', calc);
var display = nlapiGetFieldValue('custbody_cv_vsoe_calculation_disp');
var bucket = nlapiGetFieldValue('custbody_cv_vsoe_bucket');
if(bucket === 'X'){
return false;
}
if(calc > (nlapiGetFieldValue('custbody_cv_vsoe_bucket.custrecord_cv_vsoe_maintenance_rate') *1.15) || calc < ('custbody_cv_vsoe_bucket.custrecord_cv_vsoe_maintenance_rate'*0.85)){
display = '<div style="; background-color:red; color:white;font-weight:bold;text-align:center">Out of bounds</div>';
return true;
}
else{
display = '<div style="; background-color:green; color:white;font-weight:bold;text-align:center">In bounds</div>';
return true;
}
}
when I click the button I get the error TypeError undefined is not a function.
I am really not sure where to go from here, is it because the logic inside vsoeValidate is not right or am I using the wrong type of function? Any help would be great thank you!
Update: here is the screenshot of my script record!
Try passing the function name as string i.e.
form.addButton('custpage_validatevsoe', 'Validate VSOE', 'vsoeValidate');
You mentioned that you set vsoeValidate as a validateField function. Do you want this function to run when users click the button or when NetSuite's valdiateField event is fired (upon field change, before the value is stored)?
If you want this to run on NetSuite's validateField event, then the function must return true or false; it cannot return void. Right now in your logic, you have:
if (bucket = 'x') {
return;
}
if (bucket = 'x') is an assignment operation, not an equality check. This assignment operation will return 'x', which is a truthy value, so your code will enter that if-block. You then return void (undefined), so my guess is that NetSuite is trying to do something with the result returned by your function but cannot because it returned undefined.
The validateField function also gets passed a parameter that provides the ID of the field being validated.
You will also want to inject some console logging at various points so you can figure out where your script is failing instead of just trying to guess at reading syntax.
Can you provide us with a screenshot of your Script record setup?
Also since you are using a client side script, you don't need to use the pageInit event for adding a custom button.
There is a 'Buttons' subtab, under the 'Scripts' tab when you create the Script record in NetSuite. This subtab is next to the 'Libraries' subtab.
There are two columns here, 'Label' and 'Function'.
So in your case, you can just put 'Validate VSOE' in the Label field and vsoeValidate in the Function field.
Please note that if you do it this way, the button will only be shown when you are creating or editing a record.
Related
Google Apps Script supports Triggers, that pass Events to trigger functions. Unfortunately, the development environment will let you test functions with no parameter passing, so you cannot simulate an event that way. If you try, you get an error like:
ReferenceError: 'e' is not defined.
Or
TypeError: Cannot read property *...* from undefined
(where e is undefined)
One could treat the event like an optional parameter, and insert a default value into the trigger function using any of the techniques from Is there a better way to do optional function parameters in JavaScript?. But that introduces a risk that a lazy programmer (hands up if that's you!) will leave that code behind, with unintended side effects.
Surely there are better ways?
You can write a test function that passes a simulated event to your trigger function. Here's an example that tests an onEdit() trigger function. It passes an event object with all the information described for "Spreadsheet Edit Events" in Understanding Events.
To use it, set your breakpoint in your target onEdit function, select function test_onEdit and hit Debug.
/**
* Test function for onEdit. Passes an event object to simulate an edit to
* a cell in a spreadsheet.
*
* Check for updates: https://stackoverflow.com/a/16089067/1677912
*
* See https://developers.google.com/apps-script/guides/triggers/events#google_sheets_events
*/
function test_onEdit() {
onEdit({
user : Session.getActiveUser().getEmail(),
source : SpreadsheetApp.getActiveSpreadsheet(),
range : SpreadsheetApp.getActiveSpreadsheet().getActiveCell(),
value : SpreadsheetApp.getActiveSpreadsheet().getActiveCell().getValue(),
authMode : "LIMITED"
});
}
If you're curious, this was written to test the onEdit function for Google Spreadsheet conditional on three cells.
Here's a test function for Spreadsheet Form Submission events. It builds its simulated event by reading form submission data. This was originally written for Getting TypeError in onFormSubmit trigger?.
/**
* Test function for Spreadsheet Form Submit trigger functions.
* Loops through content of sheet, creating simulated Form Submit Events.
*
* Check for updates: https://stackoverflow.com/a/16089067/1677912
*
* See https://developers.google.com/apps-script/guides/triggers/events#google_sheets_events
*/
function test_onFormSubmit() {
var dataRange = SpreadsheetApp.getActiveSheet().getDataRange();
var data = dataRange.getValues();
var headers = data[0];
// Start at row 1, skipping headers in row 0
for (var row=1; row < data.length; row++) {
var e = {};
e.values = data[row].filter(Boolean); // filter: https://stackoverflow.com/a/19888749
e.range = dataRange.offset(row,0,1,data[0].length);
e.namedValues = {};
// Loop through headers to create namedValues object
// NOTE: all namedValues are arrays.
for (var col=0; col<headers.length; col++) {
e.namedValues[headers[col]] = [data[row][col]];
}
// Pass the simulated event to onFormSubmit
onFormSubmit(e);
}
}
Tips
When simulating events, take care to match the documented event objects as close as possible.
If you wish to validate the documentation, you can log the received event from your trigger function.
Logger.log( JSON.stringify( e , null, 2 ) );
In Spreadsheet form submission events:
all namedValues values are arrays.
Timestamps are Strings, and their format will be localized to the Form's locale. If read from a spreadsheet with default formatting*, they are Date objects. If your trigger function relies on the string format of the timestamp (which is a Bad Idea), take care to ensure you simulate the value appropriately.
If you've got columns in your spreadsheet that are not in your form, the technique in this script will simulate an "event" with those additional values included, which is not what you'll receive from a form submission.
As reported in Issue 4335, the values array skips over blank answers (in "new Forms" + "new Sheets"). The filter(Boolean) method is used to simulate this behavior.
*A cell formatted "plain text" will preserve the date as a string, and is not a Good Idea.
Update 2020-2021:
You don't need to use any kind of mocks events as suggested in the previous answers.
As said in the question, If you directly "run" the function in the script editor, Errors like
TypeError: Cannot read property ... from undefined
are thrown. These are not the real errors. This error is only because you ran the function without a event. If your function isn't behaving as expected, You need to figure out the actual error:
To test a trigger function,
Trigger the corresponding event manually: i.e., To test onEdit, edit a cell in sheet; To test onFormSubmit, submit a dummy form response; To test doGet, navigate your browser to the published webapp /exec url.
If there are any errors, it is logged to stackdriver. To view those logs,
In Script editor > Execution icon on the left bar(Legacy editor: View > Executions).
Alternatively, Click here > Click the project you're interested in > Click "Executions" icon on the left bar(the 4th one)
You'll find a list of executions in the executions page. Make sure to clear out any filters like "Ran as:Me" on the top left to show all executions. Click the execution you're interested in, it'll show the error that caused the trigger to fail in red.
Note: Sometimes, The logs are not visible due to bugs. This is true especially in case of webapp being run by anonymous users. In such cases, It is recommended to Switch Default Google cloud project to a standard Google cloud project and use View> Stackdriver logging directly. See here for more information.
For further debugging, You can use edit the code to add console.log(/*object you're interested in*/) after any line you're interested in to see details of that object. It is highly recommended that you stringify the object you're looking for: console.log(JSON.stringify(e)) as the log viewer has idiosyncrasies. After adding console.log(), repeat from Step 1. Repeat this cycle until you've narrowed down the problem.
Congrats! You've successfully figured out the problem and crossed the first obstacle.
2017 Update:
Debug the Event objects with Stackdriver Logging for Google Apps Script. From the menu bar in the script editor, goto:
View > Stackdriver Logging to view or stream the logs.
console.log() will write DEBUG level messages
Example onEdit():
function onEdit (e) {
var debug_e = {
authMode: e.authMode,
range: e.range.getA1Notation(),
source: e.source.getId(),
user: e.user,
value: e.value,
oldValue: e. oldValue
}
console.log({message: 'onEdit() Event Object', eventObject: debug_e});
}
Example onFormSubmit():
function onFormSubmit (e) {
var debug_e = {
authMode: e.authMode,
namedValues: e.namedValues,
range: e.range.getA1Notation(),
value: e.value
}
console.log({message: 'onFormSubmit() Event Object', eventObject: debug_e});
}
Example onChange():
function onChange (e) {
var debug_e = {
authMode: e.authMode,
changeType: changeType,
user: e.user
}
console.log({message: 'onChange() Event Object', eventObject: debug_e});
}
Then check the logs in the Stackdriver UI labeled as the message string to see the output
As an addition to the method mentioned above (Update 2020) in point 4.:
Here is a small routine which I use to trace triggered code and that has saved me a lot of time already. Also I have two windows open: One with the stackdriver (executions), and one with the code (which mostly resides in a library), so I can easily spot the culprit.
/**
*
* like Logger.log %s in text is replaced by subsequent (stringified) elements in array A
* #param {string | object} text %s in text is replaced by elements of A[], if text is not a string, it is stringified and A is ignored
* #param {object[]} A array of objects to insert in text, replaces %s
* #returns {string} text with objects from A inserted
*/
function Stringify(text, A) {
var i = 0 ;
return (typeof text == 'string') ?
text.replace(
/%s/g,
function(m) {
if( i >= A.length) return m ;
var a = A[i++] ;
return (typeof a == 'string') ? a : JSON.stringify(a) ;
} )
: (typeof text == 'object') ? JSON.stringify(text) : text ;
}
/* use Logger (or console) to display text and variables. */
function T(text) {
Logger.log.apply(Logger, arguments) ;
var Content = Stringify( text, Array.prototype.slice.call(arguments,1) ) ;
return Content ;
}
/**** EXAMPLE OF USE ***/
function onSubmitForm(e) {
T("responses:\n%s" , e.response.getItemResponses().map(r => r.getResponse()) ;
}
Google Apps Script supports Triggers, that pass Events to trigger functions. Unfortunately, the development environment will let you test functions with no parameter passing, so you cannot simulate an event that way. If you try, you get an error like:
ReferenceError: 'e' is not defined.
Or
TypeError: Cannot read property *...* from undefined
(where e is undefined)
One could treat the event like an optional parameter, and insert a default value into the trigger function using any of the techniques from Is there a better way to do optional function parameters in JavaScript?. But that introduces a risk that a lazy programmer (hands up if that's you!) will leave that code behind, with unintended side effects.
Surely there are better ways?
You can write a test function that passes a simulated event to your trigger function. Here's an example that tests an onEdit() trigger function. It passes an event object with all the information described for "Spreadsheet Edit Events" in Understanding Events.
To use it, set your breakpoint in your target onEdit function, select function test_onEdit and hit Debug.
/**
* Test function for onEdit. Passes an event object to simulate an edit to
* a cell in a spreadsheet.
*
* Check for updates: https://stackoverflow.com/a/16089067/1677912
*
* See https://developers.google.com/apps-script/guides/triggers/events#google_sheets_events
*/
function test_onEdit() {
onEdit({
user : Session.getActiveUser().getEmail(),
source : SpreadsheetApp.getActiveSpreadsheet(),
range : SpreadsheetApp.getActiveSpreadsheet().getActiveCell(),
value : SpreadsheetApp.getActiveSpreadsheet().getActiveCell().getValue(),
authMode : "LIMITED"
});
}
If you're curious, this was written to test the onEdit function for Google Spreadsheet conditional on three cells.
Here's a test function for Spreadsheet Form Submission events. It builds its simulated event by reading form submission data. This was originally written for Getting TypeError in onFormSubmit trigger?.
/**
* Test function for Spreadsheet Form Submit trigger functions.
* Loops through content of sheet, creating simulated Form Submit Events.
*
* Check for updates: https://stackoverflow.com/a/16089067/1677912
*
* See https://developers.google.com/apps-script/guides/triggers/events#google_sheets_events
*/
function test_onFormSubmit() {
var dataRange = SpreadsheetApp.getActiveSheet().getDataRange();
var data = dataRange.getValues();
var headers = data[0];
// Start at row 1, skipping headers in row 0
for (var row=1; row < data.length; row++) {
var e = {};
e.values = data[row].filter(Boolean); // filter: https://stackoverflow.com/a/19888749
e.range = dataRange.offset(row,0,1,data[0].length);
e.namedValues = {};
// Loop through headers to create namedValues object
// NOTE: all namedValues are arrays.
for (var col=0; col<headers.length; col++) {
e.namedValues[headers[col]] = [data[row][col]];
}
// Pass the simulated event to onFormSubmit
onFormSubmit(e);
}
}
Tips
When simulating events, take care to match the documented event objects as close as possible.
If you wish to validate the documentation, you can log the received event from your trigger function.
Logger.log( JSON.stringify( e , null, 2 ) );
In Spreadsheet form submission events:
all namedValues values are arrays.
Timestamps are Strings, and their format will be localized to the Form's locale. If read from a spreadsheet with default formatting*, they are Date objects. If your trigger function relies on the string format of the timestamp (which is a Bad Idea), take care to ensure you simulate the value appropriately.
If you've got columns in your spreadsheet that are not in your form, the technique in this script will simulate an "event" with those additional values included, which is not what you'll receive from a form submission.
As reported in Issue 4335, the values array skips over blank answers (in "new Forms" + "new Sheets"). The filter(Boolean) method is used to simulate this behavior.
*A cell formatted "plain text" will preserve the date as a string, and is not a Good Idea.
Update 2020-2021:
You don't need to use any kind of mocks events as suggested in the previous answers.
As said in the question, If you directly "run" the function in the script editor, Errors like
TypeError: Cannot read property ... from undefined
are thrown. These are not the real errors. This error is only because you ran the function without a event. If your function isn't behaving as expected, You need to figure out the actual error:
To test a trigger function,
Trigger the corresponding event manually: i.e., To test onEdit, edit a cell in sheet; To test onFormSubmit, submit a dummy form response; To test doGet, navigate your browser to the published webapp /exec url.
If there are any errors, it is logged to stackdriver. To view those logs,
In Script editor > Execution icon on the left bar(Legacy editor: View > Executions).
Alternatively, Click here > Click the project you're interested in > Click "Executions" icon on the left bar(the 4th one)
You'll find a list of executions in the executions page. Make sure to clear out any filters like "Ran as:Me" on the top left to show all executions. Click the execution you're interested in, it'll show the error that caused the trigger to fail in red.
Note: Sometimes, The logs are not visible due to bugs. This is true especially in case of webapp being run by anonymous users. In such cases, It is recommended to Switch Default Google cloud project to a standard Google cloud project and use View> Stackdriver logging directly. See here for more information.
For further debugging, You can use edit the code to add console.log(/*object you're interested in*/) after any line you're interested in to see details of that object. It is highly recommended that you stringify the object you're looking for: console.log(JSON.stringify(e)) as the log viewer has idiosyncrasies. After adding console.log(), repeat from Step 1. Repeat this cycle until you've narrowed down the problem.
Congrats! You've successfully figured out the problem and crossed the first obstacle.
2017 Update:
Debug the Event objects with Stackdriver Logging for Google Apps Script. From the menu bar in the script editor, goto:
View > Stackdriver Logging to view or stream the logs.
console.log() will write DEBUG level messages
Example onEdit():
function onEdit (e) {
var debug_e = {
authMode: e.authMode,
range: e.range.getA1Notation(),
source: e.source.getId(),
user: e.user,
value: e.value,
oldValue: e. oldValue
}
console.log({message: 'onEdit() Event Object', eventObject: debug_e});
}
Example onFormSubmit():
function onFormSubmit (e) {
var debug_e = {
authMode: e.authMode,
namedValues: e.namedValues,
range: e.range.getA1Notation(),
value: e.value
}
console.log({message: 'onFormSubmit() Event Object', eventObject: debug_e});
}
Example onChange():
function onChange (e) {
var debug_e = {
authMode: e.authMode,
changeType: changeType,
user: e.user
}
console.log({message: 'onChange() Event Object', eventObject: debug_e});
}
Then check the logs in the Stackdriver UI labeled as the message string to see the output
As an addition to the method mentioned above (Update 2020) in point 4.:
Here is a small routine which I use to trace triggered code and that has saved me a lot of time already. Also I have two windows open: One with the stackdriver (executions), and one with the code (which mostly resides in a library), so I can easily spot the culprit.
/**
*
* like Logger.log %s in text is replaced by subsequent (stringified) elements in array A
* #param {string | object} text %s in text is replaced by elements of A[], if text is not a string, it is stringified and A is ignored
* #param {object[]} A array of objects to insert in text, replaces %s
* #returns {string} text with objects from A inserted
*/
function Stringify(text, A) {
var i = 0 ;
return (typeof text == 'string') ?
text.replace(
/%s/g,
function(m) {
if( i >= A.length) return m ;
var a = A[i++] ;
return (typeof a == 'string') ? a : JSON.stringify(a) ;
} )
: (typeof text == 'object') ? JSON.stringify(text) : text ;
}
/* use Logger (or console) to display text and variables. */
function T(text) {
Logger.log.apply(Logger, arguments) ;
var Content = Stringify( text, Array.prototype.slice.call(arguments,1) ) ;
return Content ;
}
/**** EXAMPLE OF USE ***/
function onSubmitForm(e) {
T("responses:\n%s" , e.response.getItemResponses().map(r => r.getResponse()) ;
}
I am trying to modify a CRM form for an entity called "property".
I want to basically write an IF ELSE statement to do enable/disable a field on the form when the "new property" tab is clicked by the end user on the CRM interface.
I already have written the javascript code but I am not sure of the syntax to create a new property (a new record within the entity)?
I also tried to ID a new property record by the fact the a new property record will have the name field blank.
Please see a sample of the code below:
if(crmForm.all.new_name == null)
{
//Enable field
crmForm.all.new_interestrate.Disabled = false;
}
else
{
//Enable field
crmForm.all.new_interestrate.Disabled = true;
}
I have set this script on the OnLoad section of the form but it is not running. I postulated that this might be because I am clicking create a new property rather than loading an already existing property?
Any feedback would be appreciated.
Thanks
To anyone reading this, I solved my issue, the flaw was that the field wasn't actually null. I basically rectified this problem using the initial statement
var value = crmForm.all.new_name.datavalue
if (value < 1)
{"do something";}
This worked for my case as no name value was less than 1.
In CRM 2011, I want to be able to force a save of a form. My requirement is as below.
When the user opens the form, I check if a particular field is empty or not.
If it is empty, I want to force a save, thereby triggering a plugin which will set the field.
My current code is as below. This is in the FormOnLoad function which is associated with LoadForm.
if(checkfield == null){
Namespace.Functions.Contact.FormOnSave();
}
else{
// do nothing.
}
Now, this fails with the following line
Can't execute code from a freed script.
I have no clue as to how to proceed further. As a temporary fix, what I have done is the following
if(checkfield == null){
setTimeout('Namespace.Functions.Contact.FormOnSave()',4000);
}
else{
// do nothing.
}
Any advice on why this is happening and how can I fix it without the timeout would be really helpful?
I have had a look at this question and this, but in my case the form does get loaded or has it actually not yet loaded?
Either way, how can I force a save of a CRM form?
You can save the Form by using:
parent.Xrm.Page.data.entity.save();
But It will only trigger if
(Xrm.Page.data.entity.getIsDirty() == true)
However there is a workaround to set IsDirty property true.
Run the Javascript function on PageLoad. Try to update the value of the field which you are planning to populate through plugin:
For e.g.
function OnPageLoad()
{
var yourField = Xrm.Page.getAttribute("new_yourfield").getValue();
if(yourField == null)
{
parent.Xrm.Page.getAttribute("new_yourfield").setValue("any value");
parent.Xrm.Page.getAttribute("new_yourfield").setSubmitMode("always");
parent.Xrm.Page.data.entity.save();
}
}
You could skip all of this mumble jumbo with loading your form twice with Javascript by creating a plugin registered on the read or the entity. (Or better yet and if possible, on the Create of the entity)
The Plugin would just check for the field, and if it's empty, perform whatever logic you need it to to update the field, then save it, and return the updated field (or possibly just populate the value and don't update the database until the user performs the save)
I am trying to implement JavaScript in header of page of Oracle APEX application.
Fields in the form by default are going to be disabled if customer data exists but it can be trigger to edit mode by clicking on button 'Unlock'. By clicking button EnableItems() is run, but form is not resubmitted it is just js function that run.
function DisableItems()
{
$x_disableItem('P31_TITLE',true);
}
function EnableItems()
{
$x_disableItem('P31_TITLE',false);
}
If form is empty then all fields should be available to edit.
In Javascript >> Execute when Page Loads
I have
PageStart();
Now I need third piece of code so page would know if I want to load Enable or Disable mode. To do that I have
function PageStart(){
if ($x('P31_TEST2').value == "") {
EnableItems();
}
else {
DisableItems();
}
}
The difficulty is in second line of this code where I need to put some flag to assign value to a field or gather value from Apex field which would trigger either first or second function however I am not quite sure what would be the best practice to go around it. Any thoughts highly appreciated.
P31_START_ENABLED: hidden item, value protected = No. Provide the starting value.
For example, here the value is either 'Y' or 'N' to indicate enabled Y/N
function PageStart(){
if($v("P31_START_ENABLED")=='Y') {
EnableItems();
} else {
DisableItems();
};
};
function DisableItems()
{
$s("P31_START_ENABLED", 'N');
$x_disableItem('P31_TITLE',true);
};
function EnableItems()
{
$s("P31_START_ENABLED", 'Y');
$x_disableItem('P31_TITLE',false);
};