Suitescript: Popup when item is added on sales order - javascript

I want a popup to appear when certain items are added to a sales order in Netsuite. The popup shall appear on create and edit. In my code so far, I want the popup to appear when item number 5071, 1337 or 12345 is added to a sales order. But nothing happens when I've deployed the script. I have deployed it for all roles on the sales order record. I got the script from https://netsuiteprofessionals.com/question/popup-message-on-sales-order-when-certain-item-added/ but tweeked it a little bit.
Does anyone know what's wrong here? And how do I add so that the script logs each time it has shown a message?
/**
* #NApiVersion 2.0
* #NScriptType ClientScript
* #NModuleScope SameAccount
*/
define(['N/ui/dialog','N/record','N/currentRecord'],
function (dialog, record, currentRecord) {
function validateLine(context) {
var soRecord = context.currentRecord;
var list = context.sublistId === 'items';
var itemsArray = [507000, 124, 125];
var currentItem;
if (list) {
currentItem = soRecord.getCurrentSublistValue({
sublistId: 'items',
fieldId: 'item'
});
if (itemsArray.indexOf(currentItem) !== -1) {
dialog.alert({
title: 'Question?',
message: 'Please confirm this and that.'
}).then(success).catch(failure);
}
}
}
function success() {
return success;
}
function failure() {
return false;
}
return {
validateLine : validateLine
};
});

There is an arbitrary maximum of 10 client scripts supported. If your script is eleventh, or further down on the Client Scripts subtab of the Scripted Records page, it will not run. (This may depend whether the scripts above it are all deployed - I don't know every nuance of how the 10 are counted). You can change the order of scripts to ensure more important ones are run by Editing the Scripted Record page, selecting the line for the script you want to reorder and then drag the handle (6 dots at the left).
To work around the "10 script limit", you can refactor your client scripts to include more functionality in a single script. This can still be an issue if you have many bundles/SuiteApps installed with locked client scripts, but you can choose which ones to prioritize.
Another issue you have is here:
if (list) {
currentItem = soRecord.getCurrentSublistValue({
sublistId: 'items', //ON THIS LINE
fieldId: 'item'
});
if (itemsArray.indexOf(currentItem) !== -1) {
dialog.alert({
title: 'Question?',
message: 'Please confirm this and that.'
}).then(success).catch(failure);
}
}
Your alert function runs conditionally based on the currentItem being present. However, currentItem depends on a value being pulled from the items sublist, which does not exist. The correct sublist ID is item.

Related

Sharepoint 2013 list - how to conditionally colour java script calculated fields in sharepoint list?

I have some script that I've been working on in a Sharepoint 2013 list, it fundamentally checks some criteria against a list and returns "Pending", "Complete", "Overdue", or "Missing Due Date".
In order to check against the current date, I've used some Java script found online and modified it to work for my list. The only problem is, I'm also trying to color the text:
Red if the value is 'Overdue'
Green if the value is 'Complete'
Black for all other cases.
The code returns the values correctly and colors 'Complete' values green, but I cannot get the 'Overdue' values to the colour red.
Here is my code:
="<div style='text-align:left; color:"
&IF([Completed Date]="",""
&"<img src='/_layouts/images/blank.gif' onload=""{"
&"var SPday=new Date();"
&"SPday.setFullYear("&YEAR(DueDate)&","&MONTH(DueDate)-1&","&DAY(DueDate)&");"
&"var Days=Math.round((SPday.getTime()-new Date().getTime())/86400000);"
&"this.parentNode.innerHTML=((Days<0)?'red':'black');"
&"}"""
&"","green")
&"'>"
&IF([Completed Date]="",IF(ISBLANK(DueDate),"Missing Due date","<img src='/_layouts/images/blank.gif' onload=""{"
&"var SPday=new Date();"&"SPday.setFullYear("&YEAR(DueDate)&","&MONTH(DueDate)-1&","&DAY(DueDate)&");"
&"var Days=Math.round((SPday.getTime()-new Date().getTime())/86400000);"
&"this.parentNode.innerHTML=((Days<0)?'Overdue':'Pending');"
&"}"">"),"Complete")
&"</div>"
Can this be done this way or should I be trying another way?
For such complex logic, you'd better use CSR.
(function () {
'use strict';
var CustomCtx = {};
/**
* Initialization
*/
function init() {
CustomCtx.Templates = {};
CustomCtx.Templates.Fields = {
// update ChangeCompleted as your filed static name
'ChangeCompleted': {
'View': customDisplay
}
};
// Register the custom template
SPClientTemplates.TemplateManager.RegisterTemplateOverrides(CustomCtx);
}
/**
* Rendering template
*/
function customDisplay(ctx) {
//your logic here
}
// Run our intiialization
init();
})();
Links for your reference.
https://social.msdn.microsoft.com/Forums/office/en-US/154c3ed9-9e22-4fda-aba2-b7b12cc4a509/changing-text-color-in-sharepoint-based-on-column-value?forum=sharepointdevelopment
https://www.codeproject.com/Articles/620110/SharePoint-Client-Side-Rendering-List-Views

dont wait until function finishes to run another function

Hello I am creating this script where I create a post request that adds an item to cart and selects the size all in the backend.
As it runs it opens up a loading html and I wanted to have the current item that is being added's info added into a table in the html page.
Im not sure if this is the most efficent and fast method (please let me know if there is a better way) but what I did was I create a listener on the html/js page and send a message from the background.js page that is doing the work to that js page that is listening for the message.
What it does is that It finds the item so it sends a message of the item name, then it finds the items proper color and sends a message of the color selected, then it finds the size and selects the proper size. here is the code:
chrome.runtime.sendMessage({ carting: true, status: {
status_item: (item[0]) //item name
} });
//carting code(irrelevant to this issue)
chrome.runtime.sendMessage({ carting: true, status: {
status_color: (item[1]) //item color
} });
//more irrelevent carting code
chrome.runtime.sendMessage({ carting: true, status: {
status_size: (size_text.text) // size
} });
this is what I had in the listener page:
chrome.runtime.onMessage.addListener(function(message, sender) {
if (!message.carting) return;
var Status = message.status;
$(function() {
$("#tasks_table").append('<tr>'
+'<td>'+item+'</td>'
+'<td>'+color+'</td>'
+'<td>'+size+'</td>'
+'</tr>');
}
})
The problem with it was is that during the carting process when it yet hasn't found the color it would just constantly add undefined into the table so I found a solution to that issue by using functions:
function waitForItem(item) {
if (typeof item !== "undefined") {
$("#tasks_table").append('<tr>'+'<td>'+item+'</td>');
} else {
setTimeout(waitForItem, 10);
}
}
function waitForColor(color) {
if (typeof color !== "undefined") {
$("#tasks_table").append('<td>'+color+'</td>');
} else {
setTimeout(waitForColor, 10);
}
}
function waitForSize(size) {
if (typeof size !== "undefined") {
$("#tasks_table").append('<td>'+size+'</td>'+'</tr>');
} else {
setTimeout(waitForSize, 10);
}
}
waitForItem(item);
waitForColor(color);
waitForSize(size);
Now this did work it stopped adding the undefined into the table but now the problem is that it runs the first item function until it is completly done and then so on with each function.
When I have two items it adds the item name for the first item into the table then waits until the second item name is found and adds it too then proceeds to add all the color and the sizes. The problem with this is that my background page works in a loop. It finds the first item, then its color, then its size, then it goes onto the next item, its color, and its size.
So when It finds the first item and then the second item it adds all the rest of the color and size right at the same time. Which I don't want.
I want it to add the info into the table the same way the background page runs so the user can see how smoothly and fast it runs.

Coding custom Screen Navigation into Lightswitch HTML

I would like to have Button action event in LS HTML go slightly against the built-in navigation framework.
Specifically, to have LS navigate from one AddEditScreen to another AddEditScreen automatically, triggered by this Button event.
The trick is this - I need it to navigate to the AddEditScreen of the "next item up" in the Browse Screen List, without returning to the Browse Screen.
Example:
Select item 'ABC01' on a Browse Screen > Navigate to the AddEditScreen for 'ABC01' > edit 'ABC01' > when finished editing, trigger an event that will enable LS to navigate directly to the AddEditScreen for 'ABC02' from the Browse Screen list.
I have an open mind about what that event could be. A Button...anything at all.
I have created a Button and chose 'Write my own method'.
Does this look even close to code that will work, or will LS need to get the value of 'ABC01' from a query of some type?
myapp.AddEditHoldingInventory.Method_execute = function (screen) {
// Write code here.
var navigateToNextScreen = function (Method) {
return screen.getStrRqsNum().then(function (StrRqsNum) {
if (!!StrRqsNum) {
return myapp.applyChanges().then(function () {
var paramValue = (Number(StrRqsNum) += 1).toString();
return myapp.ShowAddEditHoldingInventory(id);
}
});
});
}
The code above is modified by me, and I am not a programmer or developer. It is snippets from pieces I have gathered and am trying to make sense of.
What the code is trying 'miserably' to achieve, is:
get the value of StrRqsNumber > save the edits made on the screen > add +1 to the value of StrRqsNumber > navigate to the AddEditSCreen of the record with the new value.
StrRqsNumber = a column with a value. It is unique and identifies an asset. This is most likely NOT the best way to achieve what I am trying to achieve, so I am here for advice. I don't have to use this as the parameter, as long as I can hit the 'next item up' from the list.
Thank you very much for any input. I will be SO stoked to get this behavior working.
This problem was solved by joshbooker. Here was the solution, which only needed minimal project specific tailoring...
"Question
Vote as helpful
0
Vote
What I want to achieve is this, for example:
Browse Screen > Select item 1 of 15 in the list > scan in the information we need on the AddEditScreen for item 1 > hit the 'trigger', and have LS automatically Save that edit, then navigate to the AddEditScreen of item 2 of 15 > and so on.
Here is a working solution for this:
/// <reference path="~/GeneratedArtifacts/viewModel.js" />
myapp.BrowseHoldingInventories.selectNextStrRqsNum_execute = function (screen)
{///custom screen method to set selected item to next StrRqsNum
//calc next num
var nextNum = (Number(screen.HoldingInventories.selectedItem.StrRqsNum) + 1).toString();
// iterate collection data to find the next item
var nextItem = msls.iterate(screen.HoldingInventories.data)
.where(function (i)
{
return i.StrRqsNum == nextNum;
}).first();
if (nextItem)
{ //if found - select the item & return true
screen.HoldingInventories.selectedItem = nextItem;
return true;
}
else
{ //not found - return false
return false;
};
};
myapp.BrowseHoldingInventories.TapMethod_execute = function (screen) {
// tap method of list item on browse screen.
//handy way to save/set scroll position
var scrollTopPosition = $(window).scrollTop();
//currently selected item
var item = screen.HoldingInventories.selectedItem;
//showAddEditScreen - pass item
// beforeShown: setup binding on FieldB
//afterClosed: if commit & select next then recurse
myapp.showAddEditHoldingInventory(item, {
beforeShown: function (addEditScreen)
{//this executes before the screen is shown
//find the trigger field
var contentItem = addEditScreen.findContentItem("FieldB");
if (contentItem)
{ //databind to catch value change
contentItem.dataBind(contentItem.bindingPath, function(newValue){
if (newValue && contentItem.oldValue && newValue != contentItem.oldValue)
{ //if change then commit - this triggers close of addEditScreen
myapp.commitChanges();
}
contentItem.oldValue = newValue;
});
}
},
afterClosed: function (addEditScreen, navigationAction)
{//this executes after the screen is closed
//scroll browse screen to where we left off
$(window).scrollTop(scrollTopPosition);
//if commit
if (navigationAction == msls.NavigateBackAction.commit)
{ //try to select next item in list
if (myapp.BrowseHoldingInventories.selectNextStrRqsNum_execute(screen) == true)
{ //next item selected then recurse
myapp.BrowseHoldingInventories.TapMethod_execute(screen);
}
}
}
});
};
"

ExtJS Change Event Listener failing to fire

I was asked to post this as a question on StackOverflow by http://twitter.com/jonathanjulian which was then retweeted by several other people. I already have an ugly solution, but am posting the original problem as requested.
So here's the back story. We have a massive database application that uses ExtJS exclusively for the client side view. We are using a GridPanel (Ext.grid.GridPanel) for the row view loaded from a remote store.
In each of our interfaces, we also have a FormPanel (Ext.form.FormPanel) displaying a form that allows a user to create or edit records from the GridPanel. The GridPanel columns are bound to the FormPanel form elements so that when a record is selected in the GridPanel, all of the values are populated in the form.
On each form, we have an input field for the table row ID (Primary Key) that is defined as such:
var editFormFields = [
{
fieldLabel: 'ID',
id: 'id_field',
name: 'id',
width: 100,
readOnly: true, // the user cannot change the ID, ever.
monitorValid: true
} /* other fields removed */
];
So, that is all fine and good. This works on all of our applications. When building a new interface, a requirement was made that we needed to use a third-party file storage API that provides an interface in the form of a small webpage that is loaded in an IFrame.
I placed the IFrame code inside of the html parameter of the FormPanel:
var editForm = new Ext.form.FormPanel({
html: '<div style="width:400px;"><iframe id="upload_iframe" src="no_upload.html" width="98%" height="300"></iframe></div>',
/* bunch of other parameters stripped for brevity */
});
So, whenever a user selects a record, I need to change the src attribute of the IFrame to the API URL of the service we are using. Something along the lines of http://uploadsite.com/upload?appname=whatever&id={$id_of_record_selected}
I initially went in to the id field (pasted above) and added a change listener.
var editFormFields = [
{
fieldLabel: 'ID',
id: 'id_field',
name: 'id',
width: 100,
readOnly: true, // the user cannot change the ID, ever.
monitorValid: true,
listeners: {
change: function(f,new_val) {
alert(new_val);
}
}
} /* other fields removed */
];
Nice and simple, except that it only worked when the user was focused on that form element. The rest of the time it failed to fire at all.
Frustrated that I was past a deadline and just needed it to work, I quickly implemented a decaying poller that checks the value. It's a horrible, ugly hack. But it works as expected.
I will paste my ugly dirty hack in an answer to this question.
"The GridPanel columns are bound to
the FormPanel form elements so that
when a record is selected in the
GridPanel, all of the values are
populated in the form."
As I understand it from the quote above, the rowclick event is what actually triggers the change to your form in the first place. To avoid polling, this could be the place to listen, and eventually raise to your custom change event.
Here is the ugly hack that I did to accomplish this problem:
var current_id_value = '';
var check_changes = function(offset) {
offset = offset || 100;
var id_value = document.getElementById('id_field').value || '';
if ( id_value && ( id_value != current_id_value ) ) {
current_id_value = id_value;
change_iframe(id_value);
} else {
offset = offset + 50;
if ( offset > 2500 ) {
offset = 2500;
}
setTimeout(function() { check_changes(offset); }, offset);
}
};
var change_iframe = function(id_value) {
if ( id_value ) {
document.getElementById('upload_iframe').src = 'http://api/upload.php?id=' + id_value;
} else {
document.getElementById('upload_iframe').src = 'no_upload.html';
}
setTimeout(function() { check_changes(100); }, 1500);
};
It's not pretty, but it works. All of the bosses are happy.
If you took a moment to read the source, you would see that the Ext.form.Field class only fires that change event in the onBlur function

JQuery/Javascript works on & off

I am using JQuery 1.3.2-min in a project to handle JavaScript animations, ajax, etc. I have stored the file on the same server as the site instead of using Google. When I run the site locally on my development machine, everything works fine in FF, IE, Opera, and Safari (all the latest versions - I work from home and I only have 1 machine for personal use and development use) except for some CSS differences between them and when I go to the live site on my machine it works fine also. I have cleared my caches and hard refreshed the page, and it still works.
This is where it gets interesting however. When I send the site to my boss to test in various OS/Browser configurations, one page doesn't work correctly, some of it works, some doesn't. Also, the client (who uses IE 8) has also confirmed that it is not completely working - in fact he has told me that the page will work fine for a hour, and then just "turn off" for a while. I have never heard of this sort of thing before, and google isn't turning too much up. I have a hunch it may partly be with JQuery's .data(), but I'm not sure.
The page is basically nested unordered lists, and three basic actions happen on the list.
The top most unordered list is set to visible (all list via css are set to display: none to keep them hidden on a fresh page request); all list items divs are given a hover action of full opacity on mouseon, and faded back to 50% opacity on mouseoff; and then whenver a paragraph is clicked, the top most unordered list in that list item is displayed.
Here is my Javascript file for the page:
$(function() {
// Set first level ul visible
$('div#pageListing ul:first').css('display', 'block');
// Disable all the hyperlinks in the list
$('div#pageListing li a').click(function() {
var obj;
obj = $(this).parent(0).parent('div:first');
highlight(obj);
return false;
});
// List Item mouse hovering
$('#pageListing li').hover(
// Mouse On
function() {
if ($(this).children('div').attr('id') !== 'activePage') {
$(this).children('div').css('opacity', 1).css('filter',
'alpha(opacity=100)');
}
}, // Mouse off
function() {
if ($(this).children('div').attr('id') !== 'activePage') {
$(this).children('div').css('opacity', 0.4).css('filter',
'alpha(opacity=40)');
}
});
// Active list item highlighting
$('#pageListing li div').click(function() {
highlight($(this));
});
// Sub-list expanding/collapsing
$('#pageListing p.subpageslink').click(function() {
// Get next list
var subTree = $(this).parent('div').next('ul');
// If list is currently active, close it, else open it.
if (subTree.data('active') != true) {
subTree.data('active', true);
subTree.show(400);
} else {
subTree.data('active', false);
subTree.hide(400);
}
});
// Double clicking of list item - edit a page
$('#pageListing li div').dblclick(function() {
var classes = $(this).attr('class');
var classArray = classes.split(' ');
var pageID = classArray[1];
editPage(pageID);
});
// Handle button clicking
$('button#addPage').click(function() {
addPage();
});
$('button#editPage').click(function() {
var div = $('div#activePage');
var classes = div.attr('class');
var classArray = classes.split(' ');
var pageID = classArray[1];
editPage(pageID);
});
$('button#delPage').click(function() {
var div = $('div#activePage')
var classes = div.attr('class');
var classArray = classes.split(' ');
var pageID = classArray[1];
delPage(pageID);
});
});
// Highlighting of page when clicked
function highlight(obj) {
// Get previous hightlighted element
// and un-highlight
var oldElement = $('div#activePage');
oldElement.css('background', 'white');
oldElement.css('opacity', 0.4).css('filter', 'alpha(opacity=40)');
oldElement.removeAttr('id');
// highlight current selection
obj.attr('id', 'activePage');
obj.css('opacity', 1).css('filter', 'alpha(opacity=100)');
obj.css('background', '#9dc0f4');
// add appropiate action buttons
$('button.pageButton').css('display', 'inline');
}
function addPage() {
window.location = "index.php?rt=cms/editPage";
}
function delPage(page) {
var confirm = window.confirm("Are you sure? Any sub-pages WILL BE deleted also.");
if (confirm) {
var url = './components/cms/controller/forms/deletePage.php';
$.ajax( {
url : url,
type : 'GET',
data : 'id=' + page,
success : function(result) {
if (!result) {
document.location = "index.php?rt=cms";
} else {
window.alert('There was a problem deleting the page');
}
}
});
}
}
function editPage(page) {
var url = "index.php?rt=cms/editPage/" + page;
window.location = url;
}
Is it possible that you are linking to (some of) the script files using a src that points to a file on your local disk/HDD? If so, that would explain why it works only on your machine, as then only your machine has access to the script file.
Thank you one and all for your suggestions. The end problem was miscommunication. I work from home, and upload my projects to a SVN server, which the boss then uses to update the live server. Somehow, the correct files were not getting updated - a communication error on my part. Another possible reason was that the page, while being declared XHTML 1.0 Strict, had something like 50 validation errors (mosting incorrectly nested UL), and I cleaned that up to 5 errors. So thank you all, but again a sad example of the importance of team work communication.

Categories

Resources