I'm trying to figure out how to change the Transaction Summary on the sales order UI.
I've been doing some research and I'm getting the impression I need to create a user event script(?)
For example: I want to add more details like Markup / Freight cost / etc.
I'm very new to this, but please let me know if I'm going in the right direction.
/**
* #NApiVersion 2.0
* #NScriptType UserEventScript
*/
define([], function() {
return {
afterSubmit : function (context) {
var salesorder = context.newRecord;
var total = salesorder.getValue('total');
var subtotal = salesorder.getValue('subtotal');
var tbl = document.getElementById("totallingtable");
log.debug('total', total);
log.debug('subtotal', subtotal);
}
};
});
NetSuite does not support access to the native UI through the DOM. You could do this on client side, but it will absolutely break the record. Therefore, I would recommend either:
Making an HTML field on the Sales Order record and writing HTML/CSS to mimic the Transaction Summary box. It would probably be best to place it in its own subtab; something like "Transaction Details", and write to it with a client script on pageInit().
Add a button to the Sales Order record on User Event beforeLoad(). Have that button open a Suitelet that essentially mimics a popup with HTML/CSS to mimic the Transaction Summary as stated in example 1.
Thinking about it, I think the second option is better.
If you need additional help, please comment back and I'll be happy to assist. I have no problem stepping through code, even if baby steps.
EDIT:
Based on the conversation below, the second option seems better for your business needs.
Before we dive into that, I did some research and believe you were trying to do something similar to this post. That approach may very well work, but is not something I would do, citing this excerpt from the NetSuite Help Documentation:
"SuiteScript does not support direct access to the NetSuite UI through the Document Object Model (DOM). You should only access the NetSuite UI by using SuiteScript APIs."
NetSuite Help Documentation: SuiteScript 2.0 Custom Pages
In addition, I'm adding a few notes to this post regarding the whole thing overall. I'll say this once here, and once at the end as a reminder. But remember, it carries for everywhere.
First, if you don't understand something, don't hesitate to ask questions. The only stupid question is the one you don't ask at any time, even if it's the i-th time you're asking as asking questions is a fundamental part of learning anything. Second, make sound coding decisions. There's no way for me to know if this code is still exactly what you need, especially since I don't know your business or the setup of your account. But, I'm sure going to try my best to help in the spirit of help.
With that said, for this example, do the following in order. Once you get the hang of it, you can work with the nuances as you see fit. I won't discuss any of the nuances here. I wish I could, but that's somewhat like explaining how to ride a bike. Here we go!
Make a sub-folder in your SuiteScripts folder called "Transaction_Details" (make sure to use the underscore). We're going to upload everything to here so it's all in one folder under the folder where we need it to be for scripts to run (that is the SuiteScripts folder). From the Administrator role that's:
Documents > Files > SuiteScripts > Add Folder
Create an HTML file that displays what you're looking for when opened on localhost. Name that file "Transaction_Details.html". NetSuite will default to this file name later, and we're going to use it a few times in our example. So, don't name the file something else. You may want to test the file in a few browsers, especially if you're supporting Internet Explorer. Since we're trying to mimic the Transaction Summary, let's make that our target. To me, it looks like a table with some CSS to make it stand out. Transaction summary is also a native NetSuite term, so we'll call this our "Transaction Details". This way, when someone says, "I don't see it in the Transaction Summary", you can say "Click the Transaction Details button which shows more details than the summary". We want to pass some numbers into this HTML which are the numbers that pertain to our "details". We don't really need syntax to do this, but it can help our readability if we, sort of, alert ourselves that whatever is supposed go in this place is marked in some way. So, we'll arbitrarily mark this as [VARIABLE_TO_REPLACE]. A sample file is below to get you started. When you have the file the way you like it, upload that to your "Transaction_Details" subfolder. From the Administrator role that's:
Documents > Files > SuiteScripts > Transaction_Details > Add File
Select Transaction_Details.html from your computer and set the folder to SuiteScripts : Transaction_Details
<!DOCTYPE html>
<html>
<head>
<title>Transaction Details</title>
<style>
<!--Any CSS you might need; background color, bold, etc.-->
</style>
</head>
<body>
<table>
<tr>
<td>Gross</td>
<td>[GROSS]</td>
</tr>
<tr>
<td>Discount</td>
<td>[DISCOUNT]</td>
</tr>
<tr>
<td>Variable To Replace</td>
<td>[VARIABLE_TO_REPLACE]</td>
</tr>
<!--Rinse and repeat-->
</table>
</body>
</html>
Create a Saved Search with whatever information you need. Let's title the Saved Search "Transaction Details" and give it an ID of "_transaction_details". You may need more than one Saved Search to get everything out. But, in many, many, situations, you can get the data out in one Saved Search. Most times, this is done using SQL. Unfortunately, I don't know everything you'll need here, so I have to leave much of this step up to you; there's a lot of nuances here. However, one thing you should consider is using a filter to filter results which only pertain to a record's Internal ID. This will ensure you're only searching for information on a record. In the Saved Search, you can pick any Internal ID of a relevant record(a Sales Order, most likely) and test on that. Once your results are how you like them, remove the Internal ID filter, as we will dynamically push this as an object in our Suitelet (coming up in step 4). Eventually, you'll need to provide access to the Saved Search. Access is controlled on the Saved Search itself using things like the Audience subtab, and the "Public" checkbox. You will know which access is best for your business. But, note that for anyone to see the Transaction Details we are building they will at least need access to the search. Access permissions is a whole other thing with NetSuite; nuances. To create a saved search from the Administrator role that's:
List > Search > Saved Searches > New > Transaction
Warning: this is a big step, but it really is all one step. Create a Suitelet that loads our HTML file and replaces our placeholder text with the proper text for that placeholder. Call this file "Transaction_Details_Suitelet.js". We're going to replace the text by running the Saved Search we created with an Internal ID filter that points to our transaction and filters the data, making it easier to extract. Now, I'm making an assumption here that the data we need came out in only one row of results. If there is more than one row, that is fine, but you'll have to do your own formatting, or give me a screenshot of your results so I can edit my answer again. An example is below to get you started. This is going to take some configuration, so it's best to get something into NetSuite that at least passes it's code inspection (which is done as you try to upload the file). Once it's uploaded, you can open the URL to the Script from its Script Deployment, which if error-free will render the HTML we've written in step 2. If it doesn't and you're getting frustrated, just upload the script as seen in example 1 below. All example 1 is saying is, "render my HTML", on the condition that your HTML is properly formatted. If that works, you know "Transaction_Details.html" is good, so you can move onto example 2. If example 2 breaks, it is likely "Transaction_Details_Suitelet.js" that is the problem. To upload the Suitelet from the Administrator role that's:
Customization > Scripting > Scripts > New > Select Transaction_Details_Suitelet.js from your computer
Set the folder to SuiteScripts : Transaction_Details
Some error checking will now occur.
If it passes, title it "Transaction Details Suitelet", and give it an ID of "_transaction_details_sl" (sl is short for Suitelet)
Click the Deployments subtab, and title the Deployment "Transaction Details Suitelet". Give it an id of "_transaction_details_sl".
So, the Script and its Deployment should mimic each other in their names. This will be important in our next step
Example 1:
/**
* #NScriptType Suitelet
* #NApiVersion 2.x
*/
define(["N/file"], function(file) {
function onRequest(context) {
if (context.request.method === "GET") {
var fileTransactionDetails = file.load({
id: "/SuiteScripts/Transaction_Details/Transaction_Details.html"
}).getContents();
//Tell NetSuite to take the HTML in our "Transaction_Details.html" file and render that. We'll navigate to the Suitelet which renders our HTML file in step 5.
context.response.write(fileTransactionDetails);
}
}
return {onRequest: onRequest}
})
Example 2
/**
* #NScriptType Suitelet
* #NApiVersion 2.x
*/
define(["N/file"], function(file) {
function onRequest(context) {
if (context.request.method === "GET") {
var fileTransactionDetails = file.load({
id: "/SuiteScripts/Transaction_Details/Transaction_Details.html"
}).getContents();
var searchTransactionDetails = search.load({
id: "customsearch_transaction_details"
});
searchTransactionDetails.filters.push(search.createFilter({
name: "internalid",
operator: search.Operator.EQUALTO,
value: context.request.params.id
}));
var resultsTransactionDetails = searchTransactionDetails.run();
resultsTransactionDetails.each(function(result) {
// getText, if you need the text representation
fileTransactionDetails = fileTransactionDetails.replace("[GROSS]", result.getText(resultsTransactionDetails.columns[0]);
// getValue, if you need something that is a date, a SQL formula result, or something "behind the scenes" like the Internal ID of an element in a list/record.
fileTransactionDetails = fileTransactionDetails.replace("[GROSS]", result.getValue(resultsTransactionDetails.columns[0]);
// I'm intentionally commenting this line out, but if you had more than one row to consider, you would return true, which tells the search, "go to the next row". For now, don't do that.
//return true;
});
//Tell NetSuite to take the HTML in our "Transaction_Details.html" file and render that. We'll navigate to the Suitelet which renders our HTML file in step 5.
context.response.write(fileTransactionDetails);
}
}
return {onRequest: onRequest}
})
Create a User Event Script that places a button, on a Transaction, on beforeLoad, in view mode only, that opens our Suitelet in step 4, where step 4 runs our search in step 3, replaces text in our HTML file from step 2, and renders the whole thing (sorry for the long sentence. but that's really the one sentence summary of the whole thing anyways). A very important note here: it is best to turn buttons on in view mode only so you can ensure all the data which you need is written to the database.
We'll need to upload the User Event script and tell the script to make the button appear on only the records selected in the User Event's Deployment tab. Let's assume its a Sales Order for now. From the Administrator role that's:
Customization > Scripting > Scripts > New > Select Transaction_Details_User_Event.js from your computer and set the folder to SuiteScripts : Transaction_Details
Some error checking will now occur.
If it passes, title it "Transaction Details User Event", and give it an ID of "_transaction_details_ue" (ue is short for User Event).
Select the Deployments tab > Applies To > Sales Order and set the ID to "_so_transaction_details_ue" which is like saying "This is the Sales Order Deployment for Transaction Details on the User Event side".
/**
* #NScriptType UserEventScript
* #NApiVersion 2.x
*/
define(["N/url"], function(url) {
function beforeLoad(context) {
if (context.type === context.UserEventType.VIEW) {
// This gives us the link to our Suitelet.
// We can pass any URL parameters to our Suitelet with params.
// One param you will definitely need is id,
// which is the id of the current record you are opening
// the transaction details from, and is used in our
// Saved Search in step 3.
// Recall that we need the id's of our Suitelet and it's Script
// Deployment to be exactly what we said above. NetSuite will prepend
// "customscript" and "customdeploy" to the id of the Suitelet and
// Deployment, respectively, so we need to do the same.
// Missing this step is a common reason why links won't open as
// expected.
// Lastly, although returnExternalUrl defaults to false, let's
// set it to false for peace of mind. We don't want to expose
// anything to the outside.
var urlTransactionDetails = url.resolveScript({
scriptId: "customscript_transaction_details_sl",
deploymentID: "customdeploy_transaction_details_sl",
params: {
id: context.newRecord.id
},
returnExternalUrl: false
});
// Because we're using at least 2.1, we can use `backticks` to
// interpolate urlTransactionDetails into window.open, which makes
// things much easier.
context.form.addButton({
id: "custpage_view_transaction_details,
label: "Transaction Details,
functionName: `window.open("${urlTransactionDetails}");`
});
}
}
return {beforeLoad: beforeLoad}
}
And that's it! Hopefully that gets you farther along. As I stated before, don't hesitate to ask questions. Also, practice good coding practices. Practice good anything for that matter. My intentions are to help you and others here. Unfortunately, this post will not solve everything, but I hope it will help you and others who stop by.
Let me know how it goes!
Related
This is the HTML code in file 1, which is calling the function. It is linked to javascript
<form id='next' action='file2.html'>
<button id="nextTwo" onclick="nextTwo()">next2</button>
</form>
This is the JS code that receives the function.
function nextTwo(){
document.getElementById('question').innerHTML=question
}
It is searching for the id before the file changes to where the id is.
Every time I press the button that calls the function it gives me:
"TypeError: Cannot set properties of null (setting 'innerHTML')"
This is because it is searching for the id of "question" in file 1, but the id is in file 2. It tries to find the id in file 1, then it switches to file 2, where the id of "question" is.
How do I make it so that is switches to file 2 first, then searches for the id.
Maybe an if statement with a condition, if the file is switched, although I don't know the how to write that.
Thanks for your help.
This is my Js code, how do I place the arrays value into the file 2 using ajax?
let ants;
let question;
let squestion;
function check() //CHECK
{
switch(1){ //different header number is fine but do '' BUT input box will still be there
case 0:
ants = ['calculations']
question=["Element Symbol that has the atomic number of... ","atomic mass of (round to nearest whole number)..."]
squestion=["1. 50","2. 2","3. 20","4. K"]
case 1:
ants = ["0 (all atoms have a charge of 0)","11","11","4","9","Be","8","8","8"]
question=["Sodium has 11 protons and an mass of 23. What is the...","An atom has an atomic# of 4 and 5 neutrons What is the...", "Oxygen has 8 electrons and a mass of 16"]
squestion=["charge","atomic#","#of electrons", "#of protons","Mass","element symbol", "#of protons", "atomic#", "#of neutrons"]
// ants = ["Sn ","He ","Ca ","39 ", "32 ","Sn ","He ","Ca",]
// question=["Element Symbol that has the atomic number of... ","atomic mass of (round to nearest whole number)..."]
// squestion=["1. 50","2. 2","3. 20","4. K"]
break;
case 2:
ants = ["Carbon", "Chlorine", "Bromine",'Br',"Li","Sc","2","8","8" ]
question=["Carbon", "Chlorine", "Bromine", "Helium",'Br',"Li","Sc" ]
squestion=[]
}
There is a better way to go about this.
By design, forms do not communicate two-way. They take the data entered by the user and carry it over to the processing file (defined by the action= parameter on the form tag). The user is navigated away from the first webpage, and the view changes to that processing file - and it isn't supposed to go back to the first file again. Forms are very basic, primitive constructs.
This is why forms have been almost entirely replaced by AJAX. AJAX is a very simple JavaScript construct (made even simpler via jQuery - TANGENT: although jQuery is no longer recommended because modern JavaScript has made the process much easier - but the jQuery $.ajax() method still works fine and there is tons of info about how to use it).
AJAX allows you to send data from a web page to a back-end (server-side) file that receives any data you send it (optional), does something (optional), and returns new data (optional). The page the user is on need not change, blink or flash. New data received from the server-side file can be received and actively used before returning control to the user... So this sounds like exactly what you need it to do.
The back-end (AJAX) processing file is written in a server-side language (PHP, NodeJS, ASP.Net, Python, etc - PHP is a very popular one for AJAX), but you did not specify which one you wish to use, which is likely why no one responded to your question sooner.
Here are some references that might help. I chose jQuery examples because the AJAX code block $.ajax( //code goes here ).done() is very simple. To do AJAX in pure JavaScript, look for information regarding the Fetch API (newest), and XmlHTTPRequest (older).
Simple jQuery example
Ajax explained with jQuery
Another simple example with explanation
Here is a pure javascript video tutorial (5 mins)
After reviewing the examples, you can construct a basic AJAX test on your side and, if you have any trouble with it, ask another question specifying which language you are trying to do this with, and showing your code so far.
For clarity:
The user will never be expected to input code. User inputs, aside from name customization (which customized names will NEVER be run through code), will be limited to button presses (which will then potentially activate functions which are waiting in the database entries).
I have one defined constructor in my javascript code, and essentially will have a database which allows the user certain inputs, be they pre-defined inputs (the majority of the time) which will execute other code or customized inputs (which would never have the potential to change anything which can execute code).
An example would be the following code: new DatabaseItem("511", ["feat", "general", "1"], "Armor Proficiency", ["description", "if(database[5][0][0].returnData().includes(0)){possible=["500","Cancel"];database[5][0][0].forEach(function tempFunction(value,index){if(value==0){possible.push(index);}});}else{database[5][0][0].addTrait("invalid");alert("No eligible targets. Please select another feat.");}"])
which would create a new DatabaseItem at position 511 with traits "feat", "general", "1"; the title "Armor Proficiency"; and data of "description" and would execute the following code when triggered:
if(database[5][0][0].returnData().includes(0)) {
possible = ["500", "Cancel"];
database[5][0][0].forEach(
function tempFunction(value, index) {
if(value==0) {possible.push(index);}
}
);
} else {
database[5][0][0].addTrait("invalid");
alert("No eligible targets. Please select another feat.");
}
Then after the user selects a choice (be it "Cancel", or one of the valid indexes) supposing that choosing this was valid in the first place, it would then execute more code based on the user's choice.
Based on what I've read, the eval() function would work (but is generally considered unsafe) but Function() may also work. What would be the recommended course of action?
Relevant information:
-this is a stand-alone webpage-based application. The framework is provided in the html file, and this is an excerpt from the js file.
-nothing is stored after the webpage is closed. Save data is generated via a savestring which implements changes the user makes and the positions of said changes, which the user is then free to copy to a text file. Invalid savestrings would be rejected by the loader.
Hello I have a small website where data is passed between pages over URL.
My question is can someone break into it and make it pass the same data always?
For example let say, when you click button one, page below is loaded.
example.com?clicked=5
Then at that page I take value 5 and get some more data from user through a form. Then pass all the data to a third page. In this page data is entered to a database. While I observe collected data I saw some unusual combinations of records. How can I verify this?
yes. as javascript is open on the website, everyone can hack it.
you will need to write some code on you backend to validade it.
always think that you user/costumer will try to hack you sytem.
so take precautions like, check if user is the user of the session, if he is logged, if he can do what he is trying to do. check if the record that he is trying get exists.
if u are using a stand alone site, that u made the entire code from the ashes, you will need to implement this things by yourself.
like using the standard php session, making the data validation etc.
or you can find some classes that other people have made, you can find a lot o this on google. as it is a common problem of web programing.
if u are using a backed framework that isnt from another world, probably already has one. sp, go check its documentation.
html:
<a id = 'button-one' name = '5'> Button One </a>
javascript:
window.onload = function() {
document.getElementById('button-one').onclick = function() {
changeURL(this.attributes.name.value);
};
};
function changeURL(data) {
location.hash = data;
}
Every time I have a new idea for an application, I start with Meteor. And every time I end up using something else. And I think it comes down to me not knowing how to do a simple database query.
Here's a hypothetical example. I want to make an app where the user types something into a box, presses a button, and then an image of whatever they typed in shows up. It uses some image search api.
<template name="image">
<input type="text" class="description"></input>
<button class="showImage"></button>
<img src="{{img}}"></img>
</template>
Seems simple enough so far. Now there isn't a way to send information to the client without putting it in a database first, as is my understanding. Let's assume we have some function addToDB that takes queries and enters the image information into the database.
Template.image.events({
'click .showImage': function() {
addToDB($('.description').val());
}
});
Great! That's still not too bad. But now to send the data back to the client...
//server.js
Meteor.publish("image", function(query) {
Images.find({q: query});
}
But wait. We can't just subscribe when the client loads, because we don't know the query yet. So maybe the event handler needs to be
Template.image.events({
'click .showImage': function() {
addToDB($('.description').val());
Deps.autorun(function() {
Meteor.subscribe("images", $('.description').val());
});
}
});
Okay, let's feed that into the template...
Template.image.img = function() {
return Images.findOne().imgsrc;
}
Nope, that results in an error because when the template is first loaded, we haven't subscribed to Images yet. So we can update the template like so:
<template name="image">
<input type="text" class="description"></input>
<button class="showImage"></button>
{{#each info}}
<img src="{{info.img}}"></img>
{{/each}}
</template>
And then change the template filling function to:
Template.image.info = function() {
return Images.find({}, {limit: 1});
}
And voila!
I spent longer than I'm willing to admit stumbling through all of that this evening. If it was just plain old node, I could've used some simple jQuery.
But there are a million amazing features that Meteor has that I really want to take advantage of. So what I'm hoping is that someone can show me the way. What mistakes did I make in this process, and in the final result? What's the nicest, cleanest, simplest way to get this done within a meteor app.
This is so complicated, within a framework that makes so many other things so simple. So how can I just make a simple database query?
Consider looking at the meteor examples they provide. All of the examples have the functionality of taking user input, managing collections, and displaying collection queries to templates.
Most of your code looks fine but you are asking several questions at once and starting from a perspective that makes it difficult to answer. Some general notes:
consider using Session to hold variables a user sets with an event.
subscribe to the data you want by passing Session variable to a Deps.autorun function
display the data you want by passing a Session variable to the template.
basic javascript rules still apply - null.someThingIWant is an error. A handy pattern is return something && something.someThingIWant;
One problem in the code above is that your publish is not returning the query results. I assume that is a typo.
What I want to do is to have a form field that allows a person to try to guess from a picture what type of bird it is, and if they get it right, it tells them they got it right and gives them the code to be able to get a discount.
Here is the code I'm using within the head tags:
formCheck()
{
var birdName = document.forms[0].birdName.value
if (birdName == "red bellied woodpecker")
alert("That's Correct! Please enjoy 10% off your next purchase by entering the code NAMETHATBIRD92 during checkout.")
else
alert("That's isn't the correct answer! Make sure your answer is very specific and keep trying, you can guess as many times as you want.")
}
Here is what I have within the body tag:
Can you name this bird?
It works here:
www.madhatwebsolutions.com/namethatbird.html
It does not work here, where I really need it to work:
http://www.wildbirdsmarketplace.com/pages/Name-That-Bird!.html
This shouldn't be JavaScript.
Any potential customer will be able to right click and view your JavaScript source and retrieve the code without bothering with the guesswork.
You'll need to query a server with the user input, and the server will need to return a response indicating whether this input is correct or not.
You might want to look at either a normal HTML form submission, or venture into AJAX
Workflow:
User enters guess into textfield
Launch a request to http://yourserver.com/check_bird.your_server_language?guess=theTextFieldValue
Server returns either a success or failure indication
Display response to client
Other things to consider: Are you going to allow your customers to guess multiple times, or restrict them? Are you going to be showing several different birds or not?
in http://www.wildbirdsmarketplace.com/pages/Name-That-Bird!.html
<script type="text/javascript" src="birdname.js"></script> refers to 404 - check the file path
don't use document.forms
var birdName = document.getElementById('birdName').value;