January 24, 2022
There are times when you need user input before you perform an action. Perhaps you are about to update many records or perform some other high consequence action and you want to give the user a chance to say "yes" to avoid costly mistakes. Fortunately Maximo provides a mechanism to request and capture user confirmation via the psdi.util.YesNoCancelException
. In this post we will explore how to request user input and then handle that input in your automation scripts.
We will provide examples using the implicit service
variable, which is an instance of the com.ibm.tivoli.maximo.script.ScriptService
class. Given that this class has undergone a lot of changes between versions, we have verified that these methods are available in Maximo 7.6.0.8, but in case you are using an earlier version we will also provide examples to replicate the functionality without the convenience of the service
class.
The first thing we need to do is define a message that will be displayed to the user. From the main navigation menu select System Configuration
then Platform Configuration
and then Database Configuration
.
From the More Actions
menu select Messages
.
Click the new row button to create a new message.
For this example we are going to set the following values:
Field | Value |
---|---|
Message Group | sharptree |
Message Key | yncexample |
Display Method | MSGBOX (default) |
Message ID Prefix | BMXZZ (custom messages) |
Display ID | checked (default) |
Value | Are you sure you want to do this? |
In the Buttons
section uncheck the OK
option and check the Yes
, No
and Cancel
options.
Click the OK
button to save the new message.
Last week we covered invoking an automation script from a button using an Action
launch point, which you can review here. Building off this, we are going to extend the COPYLABORDEF
script from last week to request confirmation before copying the values from the user's labor record.
The implicit service
variable provides the yncerror
method that takes a message group
and message key
parameters and raises a new MXApplicationYesNoCancelException
. Taking the example from last week, before we copy the values from the labor record, we are going to ask for confirmation using the message we created in the previous step.
main();function main() {// only execute the script if the mbo implicit variable is available and is based on the WORKORDER object.// also only allow this action if the work order is editable by not being in Cancel or Close status.if (mbo && mbo.isBasedOn("WORKORDER") && mbo.getInternalStatus() != 'CAN' && mbo.getInternalStatus() != 'CLOSE') {// prompt the user for confirmationservice.yncerror('sharptree', 'yncexample');// note that we will currently never get here...copyLaborDefaults();}}function copyLaborDefaults() {//...copy labor defaults}
Alternatively, you can replicate the service.yncerror
logic with the following. Note that the first parameter is COPYLABORDEF
, which is a unique identifier that will be used later to retrieve the user input.
MXApplicationYesNoCancelException = Java.type("psdi.util.MXApplicationYesNoCancelException");main();function main() {// only execute the script if the mbo implicit variable is available and is based on the WORKORDER object.// also only allow this action if the work order is editable by not being in Cancel or Close status.if (mbo && mbo.isBasedOn("WORKORDER") && mbo.getInternalStatus() != 'CAN' && mbo.getInternalStatus() != 'CLOSE') {// prompt the user for confirmationthrow new MXApplicationYesNoCancelException('COPYLABORDEF','sharptree', 'yncexample');// note that we will currently never get here...copyLaborDefaults();}}function copyLaborDefaults() {//...copy labor defaults}
We now have the confirmation being displayed, however this is not yet very useful as the the error will be raised every time the script is called. To avoid this we need to understand how to capture input from the user.
The MXApplicationYesNoCancelException
exception is special in that after it has been raised the script that raised it will be called again after the user provides input. The service
implicit variable again provides a convenience method to retrieve the user input with the yncuserinput
method. This returns an integer value representing the user's response.
The table below provides the values and their meanings.
Value | Description |
---|---|
-1 | Null, the user input has not been captured yet. |
8 | Yes |
16 | No |
2 | OK |
4 | Cancel |
These values are also provided as constants from the com.ibm.tivoli.maximo.script.ScriptService
class. You can review using constants here.
Revisiting our previous example we now check for the user input and raise the yncerror
only if the current response is empty, otherwise we handle the response.
ScriptService = Java.type("com.ibm.tivoli.maximo.script.ScriptService");main();function main() {// only execute the script if the mbo implicit variable is available and is based on the WORKORDER object.// also only allow this action if the work order is editable by not being in Cancel or Close status.if (mbo && mbo.isBasedOn("WORKORDER") && mbo.getInternalStatus() != 'CAN' && mbo.getInternalStatus() != 'CLOSE') {// Get the input that may have be returned from a previous request.var input = service.yncuserinput();// If the input is null then display the request dialog, otherwise respond to the result.switch (input) {case ScriptService.YNC_NULL:// if the user input is null then raise the exceptionservice.yncerror('sharptree', 'yncexample');return;case ScriptService.YNC_YES:// If the input was YES then proceed with copying the labor defaults.copyLaborDefaults();break;default:// else do nothing and returnreturn;}}}function copyLaborDefaults() {//...copy labor defaults}
Implementing the same functionality without using the service
variable can be seen in the following.
MXApplicationYesNoCancelException = Java.type("psdi.util.MXApplicationYesNoCancelException");MXServer = Java.type("psdi.server.MXServer");main();function main() {// only execute the script if the mbo implicit variable is available and is based on the WORKORDER object.// also only allow this action if the work order is editable by not being in Cancel or Close status.if (mbo && mbo.isBasedOn("WORKORDER") && mbo.getInternalStatus() != 'CAN' && mbo.getInternalStatus() != 'CLOSE') {// Get the input that may have be returned from a previous request.var input = MXApplicationYesNoCancelException.getUserInput('COPYLABORDEF', (MXServerRemote)MXServer.getMXServer(), mbo ? mbo.getUserInfo() : null);// If the input is null then display the request dialog, otherwise respond to the result.switch (input) {case -1:// if the user input is null then raise the exceptionthrow new MXApplicationYesNoCancelException('COPYLABORDEF','sharptree', 'yncexample');case 8:// If the input was YES then proceed with copying the labor defaults.copyLaborDefaults();break;default:// else do nothing and returnreturn;}}}function copyLaborDefaults() {//...copy labor defaults}
Now when you click the Copy Labor Defaults
button that was added to the work order application in the previous post, you now get a confirmation dialog that only executes the script if you click the Yes
button.
The complete example is provided below, note that the example provides the scriptConfig
for our Visual Studio Code extension to make deploying simple. You can get our Visual Studio Code extension here: https://marketplace.visualstudio.com/items?itemName=sharptree.maximo-script-deploy
MXServer = Java.type("psdi.server.MXServer");MboConstants = Java.type("psdi.mbo.MboConstants");MboSet = Java.type("psdi.mbo.MboSet");SqlFormat = Java.type("psdi.mbo.SqlFormat");ScriptService = Java.type("com.ibm.tivoli.maximo.script.ScriptService");main();function main() {// only execute the script if the mbo implicit variable is available and is based on the WORKORDER object.// also only allow this action if the work order is editable by not being in Cancel or Close status.if (mbo && interactive && mbo.isBasedOn("WORKORDER") && mbo.getInternalStatus() != 'CAN' && mbo.getInternalStatus() != 'CLOSE') {// Get the input that may have be returned from a previous request.var input = service.yncuserinput();// If the input is null then display the request dialog, otherwise respond to the result.switch (input) {case ScriptService.YNC_NULL:// if the user input is null then raise the exceptionservice.yncerror('sharptree', 'yncexample');case ScriptService.YNC_YES:// If the input was YES then proceed with copying the labor defaults.copyLaborDefaults();break;default:// else do nothing and returnreturn;}}}function copyLaborDefaults() {var personId = mbo.getUserInfo().getPersonId();var laborSet;try {laborSet = MXServer.getMXServer().getMboSet("LABOR", mbo.getUserInfo());var sqlf = new SqlFormat("personid = :1 and orgid = :2");sqlf.setObject(1, "LABOR", "PERSONID", personId);sqlf.setObject(2, "LABOR", "ORGID", mbo.getString("ORGID"));laborSet.setWhere(sqlf.format());// if there is a labor record associated with the current user then copy values.if (!laborSet.isEmpty()) {var labor = laborSet.getMbo(0);// copy the labor work location if not nullif (!labor.isNull("WORKLOCATION")) {// avoid issues with setting the GL or changing the asset location by skipping validation.mbo.setValue("LOCATION", labor.getString("WORKLOCATION"), MboConstants.NOVALIDATION | MboConstants.NOACCESSCHECK);}if (!labor.isNull("TYPE")) {mbo.setValue("WORKTYPE", labor.getString("TYPE"));}if (!labor.isNull("PERSON.SUPERVISOR")) {mbo.setValue("SUPERVISOR", labor.getString("PERSON.SUPERVISOR"));}}} finally {_close(laborSet);}}// use the _ in the name to indicate that this is an internal script function.function _close(mboSet) {// make sure the provided MboSet is defined and is an instance of psdi.mbo.MboSetif (typeof mboSet !== 'undefined' && mboSet instanceof MboSet) {try {// release any pending Maximo transactions.mboSet.cleanup();} catch (error) {// log the error, but there is nothing we can do to respond to it.service.log_error(error);}try {// remove any references and close the underlying JDBC ResultSet.mboSet.close();} catch (error) {// log the error, but there is nothing we can do to respond to it.service.log_error(error);}}}var scriptConfig = {"autoscript": "COPYLABORDEF","description": "Copy Labor Defaults","version": "1.0.0","active": true,"logLevel": "ERROR","scriptLaunchPoints": [{"launchPointName": "COPYLABORDEF","launchPointType": "ACTION","active": true,"description": "Copy Labor Defaults","actionName": "COPYLABORDEF"}]};
In this post we built upon the Custom Button Action post, to create and handle a Yes/No/Cancel dialog, capturing user input prior to performing the action.
If you have any questions or comments please reach out to us at [email protected]