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.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.
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
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].
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
interactive
implicit variable before accessing theUIContext
since theUIContext
will 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]