April 26, 2022
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.
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.
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.
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.
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.
Finally paste the script from source below, either JavaScript or Python depending on your preference, then click the Create button.
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
// call the main function for this scriptmain();function main() {// if a value is available in the parent attributeif (!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 ordervar 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}]};
def main():# if a value is available in the parent attributeif 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 orderparentLaborSet = 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 scriptmain()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}]}"""
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.
This is far from an ideal user experience, so let's explore how the psdi.common.context.UIContext class can help.
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.WebClientSessionprovides 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.
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.
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.
We are using an out of the box application with the Maximo demo data. Your environment and
WOTRACKapplication 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].
Alternatively, you can export the WOTRACK application XML and then simply search for the planned labor table Id as shown in the screenshot below.
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
interactiveimplicit variable before accessing theUIContextsince theUIContextwill not be available if the script is invoked from a background process.
UIContext = Java.type("psdi.common.context.UIContext")main();function main() {// if a value is available in the parent attributeif (!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 ordervar 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 UIif (interactive) {// Get the UIContext and check that it is availablecontext = UIContext.getCurrentContext();if (context) {// Get the WebClientSession and check that it is availablevar wcs = context.getWebClientSession();if (wcs) {// Get the DataBean and check that it is availabledatabean = 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}]};
from psdi.common.context import UIContextdef main():# if a value is available in the parent attributeif 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 orderparentLaborSet = 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 UIif interactive:# Get the UIContext and check that it is availablecontext = UIContext.getCurrentContext()if context is not None:# Get the WebClientSession and check that it is availablewcs = context.getWebClientSession()if wcs is not None:# Get the DataBean and check that it is availabledatabean = 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}]}"""
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.
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]