Refresh Table Data

April 26, 2022

Introduction

There are cases when writing automation scripts where we would like to refresh a table of data on the user interface (UI) without having to save and refetch the entire record hierarchy. One example of this would be a script that populates/performs operations on a set of related objects, where we want the user to review the results without saving the changes. Fortunately, the psdi.common.context.UIContext provides methods that allows us to obtain a reference to UI and refresh the data.

In this post we will explorer the psdi.common.context.UIContext class and how it is used to refresh a specific table without reloading or saving the entire record hierarchy.

Example Scenario

In our example scenario we want to copy a parent work order's planned labor to the child when the parent is added to the current work order. We want the user to be able to review the planned labor records that were copied without saving those changes first.

We will use an automation script with a Run Action, Attribute launch point on the WORKORDER.PARENT attribute to copy the parent planned labor to the child when the parent is entered. We are using this example scenario because it provides a simple action with easily observable outcomes on an out of the box screen.

Automation Script

Go to the Automation Scripts application from the navigation menu, System Configuration > Platform Configuration > Automation Scripts. From the More Action menu, select the Create menu item and then select the Script with Attribute Launch Point option.

Create Script Menu

Enter WORKORDER.PARENT.ACTION for the launch point name, Work order parent action for the description, WORKORDER for the Object and PARENT for the Attribute and then finally select Run action for the Event, then click the Next button.

Create Launch Point

Enter WORKORDER.PARENT.ACTION for the script name, Work order parent action for the description and either Nashorn or Python depending on your preference for the Script Language, then click the Next button.

Create Script

Finally paste the script from source below, either JavaScript or Python depending on your preference, then click the Create button.

Create the Script

The following scripts contain the scriptConfig variable and can be deployed using the VS Code Automation Script Deployment Utility, which can be found here https://github.com/sharptree/vscode-autoscript-deploy and here https://marketplace.visualstudio.com/items?itemName=sharptree.maximo-script-deploy

JavaScript

// call the main function for this script
main();
function main() {
// if a value is available in the parent attribute
if (!mbo.isNull("PARENT")) {
// get the parent work order using the PARENT relationship.
var parent = mbo.getMboSet("PARENT").getMbo(0);
// get the planned labor for the parent work order
var parentLaborSet = parent.getMboSet("WPLABOR");
// get the planned labor for the current work order.
var laborSet = mbo.getMboSet("SHOWPLANLABOR");
// get the first parent planned labor record.
var parentLabor = parentLaborSet.moveFirst();
// while there are parent planned labor records add them to the current planned labor.
while (parentLabor) {
labor = laborSet.add();
labor.setValue("LABORCODE", parentLabor.getString("LABORCODE"));
labor.setValue("CRAFT", parentLabor.getString("CRAFT"));
labor.setValue("QUANTITY", parentLabor.getString("QUANTITY"));
labor.setValue("SKILLLEVEL", parentLabor.getString("SKILLLEVEL"));
labor.setValue("LABORHRS", parentLabor.getString("LABORHRS"));
labor.setValue("LABORHRS", parentLabor.getString("LABORHRS"));
parentLabor = parentLaborSet.moveNext();
}
}
// NOTE: do not save the current record.
}
var scriptConfig = {
"autoscript": "WORKORDER.PARENT.ACTION",
"description": "Work order parent action",
"version": "",
"active": true,
"logLevel": "ERROR",
"scriptLaunchPoints": [
{
"launchPointName": "WORKORDER.PARENT.ACTION",
"launchPointType": "ATTRIBUTE",
"active": false,
"description": "Work order parent action",
"objectName": "WORKORDER",
"attributeName": "PARENT",
"runAction": true
}
]
};

Python

def main():
# if a value is available in the parent attribute
if not mbo.isNull("PARENT"):
# get the parent work order using the PARENT relationship.
parent = mbo.getMboSet("PARENT").getMbo(0)
# get the planned labor for the parent work order
parentLaborSet = parent.getMboSet("WPLABOR")
# get the planned labor for the current work order.
laborSet = mbo.getMboSet("SHOWPLANLABOR")
# get the first parent planned labor record.
parentLabor = parentLaborSet.moveFirst()
# while there are parent planned labor records add them to the current planned labor.
while parentLabor is not None:
labor = laborSet.add()
labor.setValue("LABORCODE", parentLabor.getString("LABORCODE"))
labor.setValue("CRAFT", parentLabor.getString("CRAFT"))
labor.setValue("QUANTITY", parentLabor.getString("QUANTITY"))
labor.setValue("SKILLLEVEL", parentLabor.getString("SKILLLEVEL"))
labor.setValue("LABORHRS", parentLabor.getString("LABORHRS"))
labor.setValue("LABORHRS", parentLabor.getString("LABORHRS"))
parentLabor = parentLaborSet.moveNext()
#NOTE: do not save the current record.
# call the main function for this script
main()
scriptConfig="""{
"autoscript": "WORKORDER.PARENT.ACTION.PY",
"description": "Work order parent action",
"version": "",
"active": true,
"logLevel": "ERROR",
"scriptLaunchPoints": [
{
"launchPointName": "WORKORDER.PARENT.ACTION",
"launchPointType": "ATTRIBUTE",
"active": true,
"description": "Work order parent action",
"objectName": "WORKORDER",
"attributeName": "PARENT",
"runAction": true
}
]
}"""

Testing

Go to the Work Order Tracking application from the navigation menu, Work Orders > Work Order Tracking. Select a work order record in WAPPR status, then navigate to the Plans tab. Select a parent work order that has one or more planned labor records and enter that value in the Parent WO field as shown below.

Note that nothing has occurred on the screen, however if you try to navigate back to the List tab, you will be prompted to save the record because the parent work order's planned labor records have been copied to the current work order by our automation script.

Parent Work Order

This is far from an ideal user experience, so let's explore how the psdi.common.context.UIContext class can help.

UIContext

The psdi.common.context.UIContext class provides a static method to obtain a singleton reference using the getCurrentContext() method. Before using this we should check that our automation script is being called from an interactive session, where a UI is present, using the interactive implicit script variable.

From the UIContext instance we can now get a singleton reference to the psdi.webclient.system.session.WebClientSession using the getWebClientSession() method, which then provides the getDataBean(String) method that finally gives us access to the psdi.webclient.system.beans.DataBean that provides the refreshTable() method that will refresh our table.

Note that the psdi.webclient.system.session.WebClientSession provides full access to Maximo's user interface, including displaying dialogs, displaying messages and navigation. We will explore these aspects in another post, but for now be aware that this is available and the Maximo JavaDocs, found here, provide a good overview of what is possible.

Data Bean Id

Before we can use the getDataBean(String) method, we need the table's data bean Id to pass to the method to get a reference to our desired DataBean.

Go to the Application Designer application from the navigation menu, System Configuration > Platform Configuration > Application Designer. Search for and select the WOTRACK application and then select the Plans tab in the designer window.

Work Order Plans Tab

Select the Labor table at the bottom of the screen in the designer window and then right click and select the Properties menu. Then note the Data Source ID attribute, which out of the box is plans_plans_labor_labor_table.

Work Order Pans Databean

We are using an out of the box application with the Maximo demo data. Your environment and WOTRACK application may be different than what is presented here. If possible, using a Maximo Demo environment would be best to follow along, or if you do not have access to a demo environment you can request one from us at [email protected].

Export

Alternatively, you can export the WOTRACK application XML and then simply search for the planned labor table Id as shown in the screenshot below. Export Databean

Updated Example Scenario

Go to the Automation Scripts application from the navigation menu, System Configuration > Platform Configuration > Automation Scripts. Select the WORKORDER.PARENT.ACTION script that we created earlier. Using the UIContext we will get a WebClientSession and then the DataBean using the Id plans_plans_labor_labor_table we found in the previous step. Finally we will add the refreshTable() method call to refresh the planned labor table.

Note that we are checking for the interactive implicit variable before accessing the UIContext since the UIContext will not be available if the script is invoked from a background process.

JavaScript

UIContext = Java.type("psdi.common.context.UIContext")
main();
function main() {
// if a value is available in the parent attribute
if (!mbo.isNull("PARENT")) {
// get the parent work order using the PARENT relationship.
var parent = mbo.getMboSet("PARENT").getMbo(0);
// get the planned labor for the parent work order
var parentLaborSet = parent.getMboSet("WPLABOR");
// get the planned labor for the current work order.
var laborSet = mbo.getMboSet("SHOWPLANLABOR");
// get the first parent planned labor record.
var parentLabor = parentLaborSet.moveFirst();
// while there are parent planned labor records add them to the current planned labor.
while (parentLabor) {
labor = laborSet.add();
labor.setValue("LABORCODE", parentLabor.getString("LABORCODE"));
labor.setValue("CRAFT", parentLabor.getString("CRAFT"));
labor.setValue("QUANTITY", parentLabor.getString("QUANTITY"));
labor.setValue("SKILLLEVEL", parentLabor.getString("SKILLLEVEL"));
labor.setValue("LABORHRS", parentLabor.getString("LABORHRS"));
labor.setValue("LABORHRS", parentLabor.getString("LABORHRS"));
parentLabor = parentLaborSet.moveNext();
}
// NOTE: do not save the current record.
// Make sure we are interactive with a UI
if (interactive) {
// Get the UIContext and check that it is available
context = UIContext.getCurrentContext();
if (context) {
// Get the WebClientSession and check that it is available
var wcs = context.getWebClientSession();
if (wcs) {
// Get the DataBean and check that it is available
databean = wcs.getDataBean("plans_plans_labor_labor_table");
if (databean) {
// Refresh the table.
databean.refreshTable();
}
}
}
}
}
}
var scriptConfig = {
"autoscript": "WORKORDER.PARENT.ACTION",
"description": "Work order parent action",
"version": "",
"active": true,
"logLevel": "ERROR",
"scriptLaunchPoints": [
{
"launchPointName": "WORKORDER.PARENT.ACTION",
"launchPointType": "ATTRIBUTE",
"active": false,
"description": "Work order parent action",
"objectName": "WORKORDER",
"attributeName": "PARENT",
"runAction": true
}
]
};

Python

from psdi.common.context import UIContext
def main():
# if a value is available in the parent attribute
if not mbo.isNull("PARENT"):
# get the parent work order using the PARENT relationship.
parent = mbo.getMboSet("PARENT").getMbo(0)
# get the planned labor for the parent work order
parentLaborSet = parent.getMboSet("WPLABOR")
# get the planned labor for the current work order.
laborSet = mbo.getMboSet("SHOWPLANLABOR")
# get the first parent planned labor record.
parentLabor = parentLaborSet.moveFirst()
# while there are parent planned labor records add them to the current planned labor.
while parentLabor is not None:
labor = laborSet.add()
labor.setValue("LABORCODE", parentLabor.getString("LABORCODE"))
labor.setValue("CRAFT", parentLabor.getString("CRAFT"))
labor.setValue("QUANTITY", parentLabor.getString("QUANTITY"))
labor.setValue("SKILLLEVEL", parentLabor.getString("SKILLLEVEL"))
labor.setValue("LABORHRS", parentLabor.getString("LABORHRS"))
labor.setValue("LABORHRS", parentLabor.getString("LABORHRS"))
parentLabor = parentLaborSet.moveNext()
# NOTE: do not save the current record.
# Make sure we are interactive with a UI
if interactive:
# Get the UIContext and check that it is available
context = UIContext.getCurrentContext()
if context is not None:
# Get the WebClientSession and check that it is available
wcs = context.getWebClientSession()
if wcs is not None:
# Get the DataBean and check that it is available
databean = wcs.getDataBean("plans_plans_labor_labor_table")
if databean is not None:
# Refresh the table.
databean.refreshTable()
main()
scriptConfig="""{
"autoscript": "WORKORDER.PARENT.ACTION.PY",
"description": "Work order parent action",
"version": "",
"active": true,
"logLevel": "ERROR",
"scriptLaunchPoints": [
{
"launchPointName": "WORKORDER.PARENT.ACTION",
"launchPointType": "ATTRIBUTE",
"active": true,
"description": "Work order parent action",
"objectName": "WORKORDER",
"attributeName": "PARENT",
"runAction": true
}
]
}"""

Testing

As before, go to the Work Order Tracking application from the navigation menu, Work Orders > Work Order Tracking. Select a work order record in WAPPR status, then navigate to the Plans tab. Select a parent work order that has one or more planned labor records and enter that value in the Parent WO field as shown below.

Note that this time the planned labor table is populated with the values from the parent work order, but the work order has not been saved. This provides the user the opportunity to modify or remove unwanted values before the record is saved.

Planned Labor Refreshed

Conclusion

In this post we demonstrated how to use the psdi.common.context.UIContext class to obtain a reference to the planned labor psdi.webclient.system.beans.DataBean, then use the refreshTable() method to display the newly created records without saving the transaction. This provides the user with the opportunity to review the updated records before they are saved and is an overall better user experience.

If you have any questions or comments please reach out to us at [email protected]

In the time it took you to read this blog post...

You could have deployed Opqo, our game-changing mobile solution for Maximo.

Opqo is simple to acquire, simple to deploy and simple to use, with clear transparent monthly pricing that is flexible to your usage.