Yes No Cancel

January 24, 2022

Introduction

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.

Display Yes/No/Cancel Message

Define the Message

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.

Database Configuration Menu

From the More Actions menu select Messages.

Messages Menu Item

Click the new row button to create a new message.

New row button

For this example we are going to set the following values:

FieldValue
Message Groupsharptree
Message Keyyncexample
Display MethodMSGBOX (default)
Message ID PrefixBMXZZ (custom messages)
Display IDchecked (default)
ValueAre you sure you want to do this?

Message Values

In the Buttons section uncheck the OK option and check the Yes, No and Cancel options.

Button Options

Click the OK button to save the new message.

Raise the YesNoCancelException

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 confirmation
service.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 confirmation
throw new MXApplicationYesNoCancelException('COPYLABORDEF','sharptree', 'yncexample');
// note that we will currently never get here...
copyLaborDefaults();
}
}
function copyLaborDefaults() {
//...copy labor defaults
}

Capture the User Input

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.

ValueDescription
-1Null, the user input has not been captured yet.
8Yes
16No
2OK
4Cancel

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 exception
service.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 return
return;
}
}
}
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 exception
throw 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 return
return;
}
}
}
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.

Yes/No/Cancel Dialog

Full Example

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 exception
service.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 return
return;
}
}
}
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 null
if (!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.MboSet
if (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"
}
]
};

Conclusion

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]

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.