1

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);


        }
    };
});
alyssajean
  • 21
  • 4

1 Answers1

2

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:

  1. 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().

  2. 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!

  1. 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
  2. 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>
  1. 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
  2. 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}
})
  1. 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!

zerecees
  • 697
  • 4
  • 13
  • @alyssajean No need to pay, that's not how helping out works! I am a little busy at the moment, but give me at least until tomorrow to edit my answer and get back to you. In the mean time, you can check [this answer](https://stackoverflow.com/questions/61052444/advanced-pdf-netsuite-group-item-tables-by-a-common-field-value/61053237#61053237) I did yesterday which render's a PDF from a button click on User Event. It's almost the same thing. Instead of loading a file, you're just hot-initializing a suitelet with some custom HTML. – zerecees Apr 06 '20 at 19:43
  • Awesome! Looking forward to it. I'll check your previous answer too. – alyssajean Apr 06 '20 at 19:44
  • No problem. If you find the answer I linked above useful, let me know and I can work off that. – zerecees Apr 06 '20 at 19:47
  • I like your idea of the 2nd option. Does your previous answer piggy back off starting that route? – alyssajean Apr 06 '20 at 20:01
  • Hi @alyssajean! Yes, my 2nd option is a large piggy back off what I linked. The design pattern in my second option is something you'll find really useful in NetSuite as well. You could do tons of things. This is a large aside, and maybe a rant, but you'll find with NetSuite the real benefit is sort of acknowledging that SuiteScript/JavaScript are great, but you have to expand your horizons past the API and the (awesome) tools they provide. Basically, with an ODBC connector and a cloud machine, or some networking, you get the data and code away. Anways, that's my rant. I'll reach out soon! – zerecees Apr 07 '20 at 00:41
  • Hopefully I can get to that point where I can fully comprehend what you said heh. For your previous answer, for the “anylibrariesyouneed”, does that refer to modules? – alyssajean Apr 07 '20 at 01:48
  • You'll get there! It just takes a few scripts. Yes, "anyLibrariesYouNeed" is a placeholder for NetSuite modules or ones which you've downloaded to your file cabinet. Examples of NetSuite modules would be `N/runtime` or `N/email`. Examples of external libraries (which you have to download to your file cabinet) might be `N/path_to/lodash_mins.js` or `N/path_to/moment_min.js` – zerecees Apr 07 '20 at 03:44
  • Hi @alyssajean, I've upated the post to reflect more of what you've talked about. Hope that helps! Let me know if you have more questions. – zerecees Apr 08 '20 at 06:01
  • Thank you! Let me check it out now!! – alyssajean Apr 08 '20 at 17:10
  • full disclosure, I am just starting now heh. keep ya updated! – alyssajean Apr 09 '20 at 20:39
  • Lol! I was just about to message back saying hope its going well (bit busy, couldn't logon yesterday). I will be on tonight answering some questions. If you need help, comment back on this thread and I'll be glad to assist! – zerecees Apr 09 '20 at 23:00
  • I'm back! So... I was pretty excited to even seen the Transaction Details button, but I'm stuck on redirecting it. When I click it, I get this page: https://pasteboard.co/J3uyLJk.png which eventually turns into this: https://pasteboard.co/J3uyVuR.png Any idea where I went wrong? I can paste a copy of the code if that will help, but it is very similar to what you gave me Thank you again :) – alyssajean Apr 12 '20 at 16:53
  • [1/2] Hello! You're so close! I believe you are not properly concatenating your URL to get to the Suitelet. First, confirm the Suitelet is working by opening its URL in the Script Deployment. From the Administrator role: `Customization > Scripting > Script Deployment > Transaction Details Suitelet > View (Left hand side of the screen) > URL (Middle right hand side of the screen)`. If that displays what you want, its the concatenation of the url in your User Event Script (UES). Try logging `urlTransactionDetails` in the UES with `log.error({title: "link": details: urlTransactionDetails});` – zerecees Apr 13 '20 at 02:32
  • [2/2] Navigate to the Execution Log on your UES Script (not your UES Script Deployment) and make sure that if you were to open that logged link, that it would be what is expected. The hard part of this is the `functionName` parameter in the UES function `context.form.addButton({...})` accepts a string which must be an actual function once it is a full string. `window.open` is a function, and that function require a URL to work properly, where the URL must be a string. As you're probably imagining, the syntax for string building is a fast way to hurl your computer from the nearest roof top. – zerecees Apr 13 '20 at 02:44
  • Follow up: I did some testing. Change the quotation syntax for `window.open` in `functionName` to `"window.open('" + link + "');"`. I've also edited this in my answer! – zerecees Apr 13 '20 at 02:56
  • Still getting the same error =/ I've added quotations to my link, could that be the problem? I had to or else it I would get errors. Like so: var urlTransactionDetails = ' https://555555.app.netsuite.com/app/site/hosting/scriptlet.nl?script=338&deploy=1' – alyssajean Apr 13 '20 at 16:34
  • It's a little hard to discern without seeing it. Could you post your user event code and I can edit it for you? Sometimes, it just helps to work backwards from the answer. I'll be sure to explain my edits so its clear! – zerecees Apr 13 '20 at 22:08
  • Sure! Is a screenshot okay with you? https://pasteboard.co/J3MtSLY.png Wasn't able to fit it in a comment. – alyssajean Apr 14 '20 at 14:31
  • Hello! Change “id=“ to “&id=“. I am online for a bit on mobile, I can make quick replies in this comment section if you are available for a bit. – zerecees Apr 14 '20 at 15:05
  • Also, just for the sake of it, change urlTransactionDetails += “&id=“ + context... to urlTransactionDetails = urlTransactionDetails + “&id=“ + String(context.newRecord.id). Perhaps NetSuite is reading the function funny using ES5. Also, I apologize but the backtick key is not available on my phone, so I cannot format my code nicely. I’ll keep an eye out for your reply! – zerecees Apr 14 '20 at 15:11
  • Just hoping to follow up. How’d it go?! – zerecees Apr 14 '20 at 22:33
  • sorry! I have yet to make those changes. I've been sick but trending up. Will let you know once I get back on it. :) – alyssajean Apr 16 '20 at 16:45
  • Ah ok, the issue is with the Suitelet then. We will need to make sure we are using the correct link to the Suitelet. Can you copy-paste your Suitelet's URL in the script deployment? The URL is the one just above and to the right of the red ellipse seen [here](https://www.fmtconsultants.com/wp-content/uploads/2016/10/NetSuite-2016.2-Script-Deployment.png). That will help me track down the problem. Also, when you open the link from the Script Deployment, do you see the HTML you coded in your file? If not, we need to step back and figure out what's not talking to what. – zerecees Apr 17 '20 at 04:41
  • yes! /app/site/hosting/scriptlet.nl?script=338&deploy=1 yes when I click the link, I see the HTML code – alyssajean Apr 17 '20 at 15:20
  • Oh awesome! Back to the UserEvent then. Let me test something offline and I’ll get back to you later today. Give me about 12 hours from now. I’m going to edit my post to use `N/url` and subsequently `url.resolveScript({...});` – zerecees Apr 18 '20 at 07:32
  • Hi @alyssajean! I've updated my answer again. You'll primarily want to look at step 5 (the User Event), but I've also clarified a few things in step 4 (the Suitelet) and step 3 (the Saved Search). As always, if you have questions, just ask! – zerecees Apr 18 '20 at 22:19
  • Hello! Any luck? – zerecees Apr 20 '20 at 23:15