Tuesday, March 8, 2022

Managing "Counters" in Boomi process

Managing "Counters" in Boomi processBoomi is very powerful and good at what it does, but sometimes it isn't clear on how to achieve certain goals. Counting a string or an element is probably one of them.

I encountered a new business requirement, that in my Salesforce-NetSuite integration pipe, I had to distinguish single-line orders from multi-line orders, then tag single-line orders with a special flag.

After digging around the web, I was able to add a counter using:
1) Dynamic Process Property
2) Data Process Shape
3) Custom Scripting within Data Process Shape

Step 1. Configure Dynamic Process Property

First, you will need to add Set Properties shape to the canvas. Add a Dynamic Process Property and give it a name. I named it 'myCounter'.

There are two methods to start your counter - by setting the value to 0, or using the updated output of the variable with the default value of 0. In the latter case, do not forget to loop your data process shape back to Set Properties shape.



Step 2. Add Data Process Shape

Now, we will need to add a Data Process shape to the canvas. The shape itself should be fairly simple, but my use case made it more complicated, because my goal is to count the number of repeating elements within a single document. And thus, I had to add Split Documents processing step to break out documents first.

Step 3. Custom Scripting

The document is finally broken down into multiple documents using Split Documents method, so now it's time for me to get the document count. You can achieve this by using Custom Scripting processing step within Data Process shape.

The original code that Boomi provides with this configuration should not be erased or modified, but we should add our counter property to this script. The tricky part is that a Dynamic Process Property's value is a string while the counter is an integer, so we need some data conversion.

Below is the final output of the code where black is the default, and red is what I have added.


import java.util.Properties;
import java.io.InputStream;
import com.boomi.execution.ExecutionUtil; // import Boomi execution library

for(int i = 0; i < dataContext.getDataCount(); i++ ) {
    InputStream is = dataContext.getStream(i);
    Properties props = dataContext.getProperties(i);
 
    // Retrieve the current myCounter value
    myCounterValue = ExecutionUtil.getDynamicProcessProperty("myCounter");
    
    // Convert myCounterValue to an integer
    int myCounterInt = Integer.parseInt(myCounterValue);
    boolean_variable = true;
    
    // Increment value by 1
    myCounterInt = myCounterInt + 1;
    
    // Convert int value back to string
    myCounterValue = Integer.toString(myCounterInt);
    
    // Set Counter
    ExecutionUtil.setDynamicProcessProperty("myCounter", myCounterValue,boolean_variable);

    dataContext.storeStream(is, props);
}

Please let me know how this worked out. If you have a better method of using counters in Boomi, please share with us!








Inventory Level Search

As I deal with users from outside of accounting & finance teams, I heard many complaints around inventory reports. For accounting & finance people whose main focus may be on the valuation of the inventory and reconciliation, inventory reports that NetSuite provides out of box may be well sufficient.

However, people who are in charge of business units that rely on the inventory throughout multiple locations, they find NetSuite's report not friendly at all, and I personally find this issue especially for operations management or hardware repair environments with large inventory (the specific organization I am referring to held more than $2MM+ worth of inventory in only one of the warehouses).



Also, one interesting issue is that if you are a long-time NetSuite customer, if you inactivated items that had inventory on hand, then it is almost impossible to reconcile quantity on hands to the inventory valuation report (although I believe NetSuite had filed defect and changed behavior maybe around 2015 or 2016, so that when you inactivate an item, all the inventory information is wiped out, instead of preventing users from inactivating items with inventory on hand - what a great workaround).

I am adding a few SQL formulas you can use on your item saved search, which can help you to generate the report you may need. If you are interested in some more examples, please let me know - I will be more than glad to investigate and share the information with you!

But frankly speaking, NetSuite's SuiteAnalytics came along such a long way that you probably don't need to use SQL but use enhanced search fields such as Inventory Details Fields...

I find this usually helpful, because if the inventory was never touching a location, then the quantity is NULL, not 0, so you can easily exclude items that you will never see at a certain location by using below formula as criteria and results:

Formula (Numeric) > not equal to 0 > Formula: NVL(DECODE({inventorylocation},'location name',NVL({location.quantityonhand},0)), 0)

Also, you can use the formula below to see any item that currently has a PO issued:

Formula (Numeric) > not equal to 0 > Formula: NVL(DECODE({inventorylocation},'location name',NVL({location.quantityonorder},0)), 0)

Of course, you will need to adjust results, and creating the results page may be more painful than it may sound. We will simply have to create each column for each location using Formula (Text) fields.

Formula (Numeric) > not equal to 0 > Formula: NVL(DECODE({inventorylocation},'location name',NVL({location.quantityonhand},0)), 0)

To make it more interesting, you can also add multiple formula fields to get results that you need:

Formula (Numeric) > Formula: NVL(DECODE({inventorylocation},'Location 1 Name',NVL({locationquantityonhand},0)),0) > Custom Label: Location 1 Quantity on Hand
Formula (Numeric) > Formula: NVL(DECODE({inventorylocation},'Location 1 Name',NVL({locationquantityonorder},0)),0) > Custom Label: Location 1 Quantity on Order
Formula (Numeric) > Formula: NVL(DECODE({inventorylocation},'Location 2 Name',NVL({locationquantityonhand},0)),0) > Custom Label: Location 2 Quantity on Hand
Formula (Numeric) > Formula: NVL(DECODE({inventorylocation},'Location 2 Name',NVL({locationquantityonorder},0)),0) > Custom Label: Location 2 Quantity on Order

Or, if you are a supply chain manager and would like to review the reorder points or preferred stock level, you may replace {locationquantityonorder} variable with {locationreorderpoint} or {locationpreferrestocklevel}.

So, here is my question back to you. If you are an operations director - or maybe a consultant or administrator helping out the operations director - how would you build the search so that your current stock and POs are enough to the orders outstanding?

I used to have a solution at my previous employer but not anymore due to the lack of test data to play with... but I will post my answer below. If this formula does not work, please let me know and I'll work in the sandbox get the test data generated and review the formula myself!







MY ANSWER:
Once you built the criteria you need, below is the formula I will use:

Formula (Numeric) > Formula: (NVL(DECODE({inventorylocation},'Location 1 Name',NVL({locationquantityonhand},0)),0) +  NVL(DECODE({inventorylocation},'Location 1 Name',NVL({locationquantityonhand},0)),0)) - (NVL(DECODE({inventorylocation},'Location 1 Name',NVL({locationquantitycommitted},0)),0) + NVL(DECODE({inventorylocation},'Location 1 Name',NVL({locationquantitybackordered},0)),0)) 

Then I will go to Highlighting tab, add the exact same formula to the condition to flag any items that may run into inventory issues!



I hope this post helps you with inventory reporting issues you may have at your organization. What are some of the other key challenges you encounter from day-to-day usage of NetSuite? I'm waiting to hear from you!

Pulling Sales Orders with Related Transactions in Search and SQL

Sometimes, you need to pull sales order information and also include the estimate and/or invoice, and sometimes even an item fulfillment. For advanced users, it is very easy to build such search, but it may be more complicated than it sounds if the advanced user is not available.

Using Saved Search:

Disclaimer: Due to how transactions and transaction lines in NetSuite are linked, you will get duplicate lines even after following many of SuiteAnswers articles, especially if you are trying to show many different linked transactions in our search. My instruction below returns the best result in my personal opinion. Also, please note that this method will NOT work if multiple transactions of the same type are associated with the sales order.

1) Go to Reports > Saved Searches > All Saved Searches > New (or your preferred method to get to New Saved Search screen)
2) From New Saved Search screen, select Transaction.
3) Under Criteria tab, add the following filter - you can also add Main Line = T or F, depending on the type of information you are trying to extract, but keep in mind that estimate information is at header only, created
   Type = Sales Order
4) Under Results tab, add the necessary fields, and you can add the fields listed below for Invoice, and Estimate information:
   For Invoices:
   Billing Transaction
   For Item Fulfillment:
   Fulfilling/Receiving Transaction
   For Estimate (you can use one of the two):
   Applied To Transaction (Will not work if Main Line = F is set under criteria)
   Created From (will work regardless of criteria)
5) Adding an Item Fulfillment column is little more tedious than it seems. NetSuite offers "Fulfilling/Receiving Transaction"field, but it also pulls all other types of transactions (i.e. Invoice) that is associated with this sales order, duplicating every line. I am not sure if this is a defect, but I recommend using Formula (Text) field:
   Formula (Text) >  Formula: CASE WHEN {fulfillingtransaction.type} = 'Item Fulfillment' THEN {fulfillingtransaction} END
6) Because how  transactions  to how each line is linked, we will need to aggregate the result fields. I recommend summarizing them with "Maximum"


If you are familiar with SQL, it is probably easier to obtain such information, except one pain point - because all transactions regardless of the type are stored in one huge TRANSACTIONS table, you will need to join the TRANSACTIONS table to TRANSACTIONS table. Below is a sample SQL that I would use.

SELECT t.tranid AS 'Sales Order #'
       ,e.tranid AS 'Estimate #'
       ,i.tranid AS 'Invoice #'
       ,f.tranid AS 'Item Fulfillment #'

FROM TRANSACTIONS t
    JOIN TRANSACTIONS e ON t.created_from_id = e.transaction_id
    JOIN TRANSACTIONS i ON t.transaction_id = i.created_from_id AND i.transaction_type = 'Invoice'
    JOIN TRANSACTIONS f ON t.transaction_id = f.created_from_id AND f.transaction_type = 'Item Fulfillment'


WHERE t.transaction_type = 'Sales Order'


I hope this post helps you with transaction reporting reporting issues you may have at your organization. What are some of the other key challenges you encounter from day-to-day usage of NetSuite? I'm waiting to hear from you!

Thursday, October 11, 2018

NetSuite ODBC and JDBC via DBeaver

In a new data warehousing project that I'm involved with, I ran into some challenges connecting to and pulling data from NetSuite ODBC server using SuiteAnalytics Connect. It was quite a journey to finally find the right tool, as many SQL Clients - including the one from Oracle and Microsoft - were not as robust and flexible to connect to NetSuite's ODBC.

After digging around, I found an "Universal Database Tool" called "DBeaver" that is free for developers, SQL programmers, database administrators and analysts. It was very flexible that I could connect to any database if I had the JDBC available.

And I wanted to share these steps (as detailed as possible), hoping that no one else runs into the issues I had and waste so much time.

These steps are based on MacOS, but it's not very different on Windows either. If you need the guide to connect to NetSuite using DBeaver on Windows, please let me know.


1) Download and install DBeaver Community from https://dbeaver.io/

2) Download and install SuiteAnalytics Connect - refer to SuiteAnswers article 38965 and related topics. I downloaded and extracted JDBC for MacOS since ODBC isn't available.

3) Launch DBeaver Community. We need to add NetSuite driver. Go to Database > Driver Manager, and click New.

4) Give it a proper name and description. Set Class Name to com.netsuite.jdbc.openaccess.OpenAccessDriver and leave the URL Template and Default Port blank. Click "Add File" and choose the NQjc.jar file that was extracted from step 2). Click OK.



5) Once the driver is added, you can hit OK to exit from the driver manager window. Click on New Connection or go to Database > New Connection. Select the driver you created in step 4), and hit Next.

6) Enter below URL to JDBC URL field, and add your user name and password. Account Id and Role Id can be found on "Set Up SuiteAnalytics Connect" page.

jdbc:ns://odbcserver.na1.netsuite.com:1708;ServerDataSource=NetSuite.com;encrypted=1;Ciphersuites=TLS_RSA_WITH_AES_128_CBC_SHA;CustomProperties=(AccountID={accountId};RoleID={roleId})



7) When you click on Test Connection... you will see a Success box if things went well!