Automatically Email Vendor on Approval of Purchase Order in IBM Maximo

Automate emails to vendors from approved purchase orders in Maximo CMMS

This article will walk an IBM Maximo administrator through the steps necessary to send a purchase order by email to a vendor as an attachment. It is a common requirement by customers to be able to automatically send the purchase order to vendors through email. Some of the benefits of doing this are:

  • Standardize the way that approved purchase orders are communicated to vendors. A common template can be used for the email subject and body, as well as a common workflow for when the communication is sent.
  • Reduce the time taken by procurement agents in generating reports, saving them locally, finding appropriate contact information, and typing up an email.
  • When IBM Maximo sends the email, a copy of the email that was sent is stored in the Communication Log along with the purchase order. If you ever need to know when the email was sent and to whom, the documentation of the email is in IBM Maximo attached directly to the purchase order. No more searching through email to find the communication.

To achieve this, we will deploy an automation script in IBM Maximo that will generate the report and send the email. Before we get to the script, we need to ensure that the following prerequisites are met:

  1. The email address of the vendor should be recorded in the Companies application on the Contacts tab. The script below will use the email address of the Primary Contact (set on the Addresses tab) but can be adjusted to include a larger or more targeted set of email addresses. However, they must be accessible from somewhere in the system by the script.
  2. A Communication Template should be created and activated to be used as the email subject and body of the communication. By default, I’ve named the template POTOVENDOR, but you can name it anything you want and change it in the script.
  3. A BIRT report that represents the purchase order to be sent to vendors. By default, the auto script uses the poprint.rptdesign report that comes with IBM Maximo. This can be changed in the script to a custom report but be sure to match up the report parameters if necessary.

The trigger point for the script below is a status change of the purchase order to APPR. However, this could just as easily be triggered from IBM Maximo workflow, escalations, or other means.

Here is the automation script needed to make this happen:

from com.ibm.tivoli.maximo.report.birt.queue import ReportQueueService
from com.ibm.tivoli.maximo.report.birt.runtime import ReportParameterData
from psdi.mbo import SqlFormat

# Get the current user's information
userInfo = mbo.getUserInfo()
locale = userInfo.getLocale()

# If the PO Vendor does not have a contact with email, then kindly exit
if not mbo.isNull("VENDOR.PRIMARYCONTACT.EMAIL"):
    emailTo = mbo.getString("VENDOR.PRIMARYCONTACT.EMAIL")
    # Append the current user's email address to the chain to ensure delivery
    if userInfo.getEmail() and not userInfo.getEmail() == "":
        emailTo = emailTo + ", " + userInfo.getEmail()
    
    # Create default message subject and body
    # These will be replaced by a communication template if one is found by the code below
    emailSubject = "Purchase Order"
    emailComments = "Please acknowledge receipt of this Purchase Order."
    
    # Change this value to a valid Maximo Communication Template ID
    commTemplateID = "POTOVENDOR"
    
    # Get the communication template
    commTemplateClause = SqlFormat(mbo, "templateid = :1")
    commTemplateClause.setObject(1, "COMMTEMPLATE", "TEMPLATEID", commTemplateID)
    commTemplateSet = mbo.getMboSet("$potovendor", "COMMTEMPLATE", commTemplateClause.format())
    commTemplateMbo = commTemplateSet.getMbo(0)
    if commTemplateMbo:
        # Build the email subject and body from the Communication Template
        sql = SqlFormat(mbo, commTemplateMbo.getString("SUBJECT"))
        sql.setIgnoreUnresolved(True)
        emailSubject = sql.resolveContent()
        sql = SqlFormat(mbo, commTemplateMbo.getString("MESSAGE"))
        sql.setIgnoreUnresolved(True)
        emailComments = sql.resolveContent()
    
    # Build the Report Parameters
    parameterData = ReportParameterData()
    parameterData.addParameter("appname", "PO")
    parameterData.addParameter("paramdelimiter", "")
    parameterData.addParameter("paramstring", "")
    parameterData.addParameter("where", "(po.poid = " + str(mbo.getLong("POID")) + ")")
    
    # Queue the Report to Run
    queueManager = ReportQueueService.getQueueService()
    queueManager.queueReport("poprint.rptdesign", "PO", userInfo.getUserName(), emailTo, emailSubject, emailComments, parameterData, locale.getCountry(), locale.getLanguage(), locale.getVariant(), userInfo.getTimeZone().getID(), userInfo.getLangCode(), long(0), userInfo)

MBOs in Automation Scripts: Exception Handling

In our previous posts in this series, we talked about how an MboSet is a collection of Mbo objects. In this article, we’ll analyze the different types of exceptions that can be thrown and how each can be utilized.

Most methods that can be called through the Business Components can throw either a RemoteException and/or an MXException.

A RemoteException is thrown by the Java RMI system when an error occurs during remote communication. The exact nature of the error may be determined by checking the Exception object’s class – which may be one of seventeen subclasses of RemoteException.

For more details of these exceptions, refer to the documentation in the Java Development Kit.

The MXException is similar to the RemoteException in that the MXException is a super class for all related exceptions. The actual exceptions thrown will be instances of one of the following subclasses:

  • This is usually a fatal error from which the client cannot recover. It is usually a problem with either the server configuration or the database configuration. Examples of errors causing this exception are “Out of Memory on Server” or a SQL error. Two subclasses of this class have also been introduced:
    • Thrown when a client or server asks for a Mbo that is not known to the system. This is a fatal error that normally points to a bug in the system or an incorrectly configured data dictionary.
    • Thrown when the server, which should have an object running, does not respond. This is usually caused by an incorrect configuration of the remote configuration file or indicates that the server running the object is unavailable for some reason.
  • This is thrown as a result of any of the many possible application-level errors, most of which are not fatal. Such exceptions should be caught by the client and rectified by the logic of the calling program. Examples of reasons why this exception might be thrown are:
    • Trying to set the value of a date attribute to the characters “ABC”.
    • Trying to set the “eqnum” attribute in a WO object to an invalid equipment number.
  • Thrown when an attempt is made to set an attribute that is read only or access a method that is currently not available. Methods may be unavailable because of security restrictions or because the object is not currently in the correct state.

More information about an exception can be retrieved by accessing the getMessage(), getErrorGroup() and getErrorKey() methods in the thrown object. These methods constitute two different approaches to identifying the exact cause of the exception.

The getMessage() method returns a string which contains a message describing the nature of the error. A message from a nested exception, if there was one, is included here as well. Such messages are intended to be for the benefit of human users. If the intention is that the code itself should handle the recovery from the error, then it is more appropriate to call the other two methods.

The error group, returned by getErrorGroup(), usually corresponds to the name of a text file in the RESOURCES\DEFAULTS directory. For example, if the error group is “workorder”, then the textual error description is in resources\default\workorder.txt.

The error key, returned by getErrorKey(), identifies an exact line in the text file that describes the error. For example, “addstatus” identifies a line in workorder.txt that has the line of text “addstatus=WorkOrder status cannot be added without a work order”.

When the intention is that the code itself should recover from an error, it is far easier to use an error key as input for a condition statement than a message. Note that the error description pointed to by the error key is not necessarily the same as the message returned by getMessage().

Exceptions may also provide greater detail in the form of parameters. These are used to supply information for a more general type of exception. For example, if an attempt is made to call the setValue() method using an invalid attribute name then the error group “system” and the error key “noattribute” would be returned. The exception thrown would also contain as part of its parameter list the name of the attribute that could not be found. To access these parameters, use the methods hasParameters() and getParameters().

An MXException may be thrown as the result of another exception occurring in the system. An example of this is a SQLException. These exceptions are not thrown directly from the methods because it would make method signatures complicated. The programmer can gain access to the nested exception by referencing the member “detail”, which is public, of the thrown exception.

A code sample demonstrating the use of an MXException follows:

MXSession s = MXSession.getSession(); s.setHost(hostname); s.setUserName(username); s.setPassword(password); // Display any problems with the connect to the user. try { System.out.println(“Connecting to (” + s.getHost() + “/”+ s.getServer() + “)” ); s.connect(); } catch(MXAccessException ma) { String group = ma.getErrorGroup(); String key = ma.getErrorKey(); System.out.println(“Group : ” + group + “ Key : ” + key + “ Message :” + ma.getMessage()); }

Updating End Points and Reloading Cache in Automation Script

In Maximo you can use End Points to point to External APIs to pull in data. You can invoke those End Points in a variety of ways, including inside an Automation Script. Take the following code as an example:

handler = Router.getHandler('ENDPOINTNAME');
responseBytes = handler.invoke(null, null);

You can then parse the response that comes back and use that data in any way that meets your needs. Recently we were working with an external API that required an initial login to capture an API Key to be used with future calls. We called the Login API and then grabbed an API Key from the response object. We then needed to update a different End Point to utilize this API Key as an HTTP Header in future calls.

This appeared to work in Maximo as we would see the data show up on the updated End Point. However, each time we invoked the End Point it would throw an error that it was not able to connect. We would then restart Maximo and everything would start working. What we found is that End Point data in Maximo is cached. After updating an End Point, we needed to reload the End Point cache to make a successful connection. To do that we added a line in the automation script after we updated and saved the End Point with the new API Key. The line that is needed is:

MXServer.getMXServer().reloadMaximoCache(EndPointCache.getInstance().getName(), true);

After running this code we can now successfully call our updated End Point. Be aware that there are several similar objects that are cached when Maximo starts up, such as Relationships, Integration information, some Domain information, etc. If updates are made to those records, you may need to refresh the Maximo cache for those records as well.

Hope this helps!

MBOs in Automation Scripts: Adding New Objects to the Collection

In our previous posts in this series, we talked about how an MboSet is a collection of Mbo objects. This is analogous to a spreadsheet of data representing an MboSet, and a single row within that spreadsheet representing an Mbo. This article will discuss how to update single attributes on an Mbo, or in our spreadsheet analogy, update data within a single cell within a row.

If there is a need to add a new object to a collection, the add() method may be on an MboSet, as in the following example:

newWo = woSet.add()

This returns a reference to the newly added object and sets the current index in the collection to point to it. The location of the new object within the collection is not guaranteed, so no assumptions should be made as to where it has been placed relative to the previous current index. You can use the getString() method to fetch default or autokey values for particular attributes. One of the best examples of an autokey value is the “wonum” attribute, which must be unique as the principal identifier of a work order. These values are set automatically by Maximo as part of the add() method.

Once a new object has been created, the attributes of the object can be altered by using the setValue() method. This method call can be performed directly on the newly added object:

newWo.setValue("assetnum", "11430")

As mentioned previously, changing an attribute value may result in other attribute values being updated.

Once all values have been set, the save() method should then be called:

woSet.save()

The save() method may throw an exception, as was discussed in the section on modifying objects. The most common exception thrown by the saving of a newly added object results from a required attribute not being set. The exception returned in this case is of type MXApplicationException with an error group and key of “system” and “null”, respectively.

Our next article will focus on exception handling with Mbos and MboSets.

MBOs in Automation Scripts: Update Attributes of an Mbo

In our previous posts in this series, we talked about how an MboSet is a collection of Mbo objects. This is analogous to a spreadsheet of data representing an MboSet, and a single row within that spreadsheet representing an Mbo. This article will discuss how to update single attributes on an Mbo, or in our spreadsheet analogy, update data within a single cell within a row.

To modify an object in the collection, the object must first be found. Most of the time, the launch points in automation scripts will supply an implicit variable called mbo that refers to the current record. Alternatively, you can get a handle to an Mbo from an MboSet as described in previous articles of this series – remember the setWhere() and moveFirst() methods. Once you have an Mbo, changes are made using the setValue() method. As is the case with other methods, setValue() is a member of both the Mbo and MboSet classes.

Our first example changes the description of the current Mbo (a work order) by calling the mbo variable’s version of the setValue() method.

mbo.setValue("DESCRIPTION", "Changed the description")

In the setValue() method, passing the second argument as a string works for all attributes, regardless of their underlying data types. For example, if an attribute is a numeric type, passing the string “1” is allowed. There are also setValue() method calls that take the long, int, Date, boolean, float, or double data types as the second argument.

This call will throw an exception if the underlying data type does not match the type of the value passed in the call. For example the following code does not work.

mbo.setValue("description", 1.0)

A call to set the value of a particular attribute may result in the values of other attributes changing. For example, setting the “assetnum” attribute of a work order may result in the “location” attribute being updated as well. To check the new value, getString() could be used on the “location” attribute.

Once all values have been modified, the save() method can then be called.

mbo.getThisMboSet().save()

This saves and commits all changes to the database. In most cases, an explicit call to save() is not necessary. In cases where the automation script fires before the save event, the save event will trigger automatically after the execution of the script.

If there is an error during the save, an exception is thrown and the changes are not saved. In that case, the collection is left in exactly the same state as prior to the save() call. This allows appropriate action to be taken and the save() call to be resubmitted.

There is a third argument that can be passed to the setValue() method, which is the Access Modifier. This should only be used when you are certain of the implications of using it. In essence, standard application logic can be bypassed by using access modifiers, which is why they should be used only when necessary. Standard access modifiers are:

  1. NOACCESSCHECK: Suppresses the “Field is Read Only” message and performs the update regardless of whether the field is editable or not.
  2. NOVALIDATION: Update the field without validating the data being entered.
  3. NOACTION: Do not perform related system actions when updating. For example, do not update the location on a work order when updating the asset.

These access modifiers can be used on an update by themselves or combined together. For example, to update a field even though Maximo has the field marked as read only:

mbo.setValue("DESCRIPTION", "Update a read only field", mbo.NOACCESSCHECK)

To update a field and not perform the validation and action events:

mbo.setValue("DESCRIPTION", "Update a field without validation or action", mbo.NOVALIDATION|mbo.NOACTION)

Putting all three together:

mbo.setValue("DESCRIPTION", "Please be careful doing this", mbo.NOACCESSCHECK|mbo.NOVALIDATION|mbo.NOACTION)

A list of attributes whose values may be modified and the objects to which they belong can be found in the MAXATTRIBUTE table in the MAXIMO database. The OBJECTNAME column of this table corresponds to the object name. The ATTRIBUTENAME column corresponds to the attribute name.

Our next article will focus on how to add new Mbo records into an MboSet.

Auto-Generating DBC Files

In the ongoing effort to use cool and interesting tools for application and database updating it has been very nice to see the geninsertdbc.bat tool put into practical use. Harnessing these tools to create DBC files can be very advantageous if you are still getting all the available commands and syntax down to master them. So, let’s look at a scenario where I needed to update a Maximo automation script.

The required solution is to update a pre-existing automation script with new code for the source field in the autoscript table as well as update the scriptlanguage field. The caveat is that the script language (python) requires proper formatting to execute correctly. In the first attempt I tried to update the automation script via a freeform insert in the DBC’s statements section using SQL line breaks and indentation syntax.

 

While this did achieve the outward appearance that I wanted Maximo was not “sold” on the line spacing and indentation of the ( CHAR(13) or CHAR(9) ) SQL syntax and the automation script would not run after the server(s) were restarted.

Now, to get to the fun part. An alternate method was suggested on how to get this information into Maximo via the geninsertdbc.bat tool. IBM has tools available that can auto generate DBC files based on parameters that you give the output tool at the command prompt. I reset the automation script’s source code with the python source code that was previously working, and I opened an administrative command prompt then navigated to the ..\maximo\tools\maximo\internal directory under the Maximo installation folders.

I ran the following command: C:\IBM\SMP\maximo\tools\maximo\internal>geninsertdbc.bat -tautoscript -w"autoscript = 'ITEMDELETION'" -ftest2 -ca3jgroup

To break this down lets look at the parameters given for the command.

-t: will pull from the table

-w: will specify a where clause

-f: will name the output file

-c: will point the file to a directory of your choosing

 

After the geninsertdbc.bat tool is run there will be an output file created which will look similar to this.

In this file we have the column information extracted from the database and formatted in the XML layout of a DBC file ready to be either edited or re-added to Maximo via the runscriptfile.bat command. What I was able to use from this file was the character string that the geninsertdbc.bat placed around the source string (
) which is the character string that allows for spacing and line breaks while inserting into databases via the DBC file method.

I then took this info and created a DBC file that would update as opposed to insert into the database. The source COLUMNVALUE took some effort to get the correct spacing and breaks in place, but when you run the script and restart the server(s) your automation script will run without any further efforts.

This really is just a scratch of the surface of what you can do with auto generating DBC files. Further uses can only truly be explored/acknowledged by trying this method on other tables to see the results that may come from it and applying the built-in functionality detailed in files like the script.dtd file for manipulating the data to be added/changed.

An outstanding source for learning about DBC files is located on this IBM Developer Works page. I encourage anyone interested in this to go through this content and then try to apply it to your day to day tasks.

MBOs in Automation Scripts: Reading Multiple Attributes of an MboSet

In our previous posts in this series, we talked about how an MboSet is a collection of Mbo objects. This is analogous to a spreadsheet of data representing an MboSet, and a single row within that spreadsheet representing an Mbo. This article will discuss how to read multiple attributes from an Mbo, or in our spreadsheet analogy, look at data within multiple cells within a single row.

Because most of the objects in Maximo possess many attributes, a long series of getString() method calls might be required to retrieve all the necessary information in a given situation. To make such retrieval more convenient, a method called getMboValueData() has been provided. It allows the programmer to fetch many data items in a single operation by specifying a list of attributes and, if desired, a set of rows in the collection. The result of such a call is a one- or two-dimensional array of MboValueData objects. Another advantage of this approach is that it speeds up processing over a network. When a number of data items are retrieved in a single call over a network rather than many, the efficiency advantages are self-evident.

The getMboValueData() method is available in several versions; as with many other methods it is a member of both the MboSet and the Mbo classes. The general syntax of the most-used version is as follows:

valueDataList = mbo.getThisMboSet().getMboValueData(attributes[])

or

valueDataList = mbo.getMboValueData(attributes[])

The attributes variable above is an array of strings containing a list of attribute names (e.g. “wonum”, “description”, “assetnum”, or “asset.description”). The “asset.description” attribute is an example of “dot notation” using Maximo relationships (more on relationships in a subsequent article). Dot notation is a shortcut that allows you to retrieve data from a related object without accessing the related object itself. For example, if a Work Order object has a value entered in its “assetnum” attribute, passing “asset.description” to either getString() or getMboValueData() will fetch the specified Asset’s description. Thus, by combining dot notation with getMboValueData(), data can be simultaneously retrieved in one method call even from objects that are in different Maximo Business Objects. This is an important part of the convenience that using getMboValueData() confers.

The return from the getMboValueData() method is an array of MboValueData objects. Each one contains the value of the attribute specified in the corresponding position in the attributes parameter. There are several methods which allow retrieval of this data value; the most often used, getData(), returns the data as a string. Not only can the data value be recovered; there are also methods which return information about the attribute itself, such as its maximum length, whether it is read-only, etc. This additional information is very useful in many situations.

A code sample follows:

attributeList = [ "wonum", "description", "assetnum", "asset.description" ]
// Assume the mbo is a Work Order and that the Work Order object has an Asset associated with it.
valueData = mbo.getMboValueData(attributeList);
print "The description of Work Order " + valueData[0].getData() + " is " + valueData[1].getData()
print "The Asset associated with Work Order " + valueData[0].getData() + " has identifier " + valueData[2].getData() + " and description " + valueData[3].getData()

If any of the attributes specified in the array of attribute names (attributeList in the above example) does not exist, a null value is placed in the corresponding position of the returned array. Similarly, if any of the dot notation entries in the list refer to nonexistent related objects, nulls are returned in those positions as well. In the above example, if the mbo variable has no value specified in its “assetnum” attribute, then null would be returned in the position where “asset.description” is entered in attributeList.

There are two other versions of the getMboValueData() method. One is a special case of the version described above. The syntax is:

valueData = mbo.getThisMboSet().getMboValueData(attribute)

or

valueData = mbo.getMboValueData(attribute)

The argument is a string which is a single attribute name and the return is a single MboValueData object. As above, this method is a member of both the MboSet and Mbo classes. Even though it returns less data than the more general versions, it may still be preferable in certain situations. For example, if the desired result is information about a single attribute (e.g. read-only, length, etc.) of some object, then it is sufficient to apply this method to that single attribute rather than many.

The remaining version is more general in nature. This is a member of only the MboSet class and allows the retrieval of data not only for multiple attributes but for multiple objects as well. It is used when many objects are to be processed at once, such as during the generation of a report or the filling of a table. The syntax is:

valueDataList = mbo.getThisMboSet().getMboValueData(firstRow, rowCount, attributes[])

The firstRow and rowCount variables are integer arguments; they specify the first row and the number of rows in the row set to be retrieved, respectively. The most commonly used values are 0 and mbo.getThisMboSet().count(), which fetch data for the entire collection.

The attributes variable is an array of strings containing a list of attribute names, as above. The return is a two-dimensional array of MboValueData objects. A code sample follows.

attributeList[] = [ "wonum", "description", "statusdate" ]
// Assume the woSet collection is non-empty
woCount = mbo.getThisMboSet().count()
valueData = mbo.getThisMboSet().getMboValueData(0, woCount, attributeList)
for i in range(len(valueData)):
    print "The description of Work Order " + valueData[i][0].getData() + " is " + valueData[i][1].getData()
    print "The Status Date of Work Order " + valueData[i][0].getData() + " is " + valueData[i][2].getData()
}

That’s it for now. Our next article will focus on how to modify attribute values of an Mbo in an MboSet.

Calling Automation Scripts in a Federated MBO Endpoint

With the new version of Maximo we are now able to use Federated MBOs. Federated MBOs allow us to show live data from external systems without storing the data in Maximo. In that call you can have a static URL or you can dynamically update the URL to pull different data every time. For instance, if you only want to see the last 24 hours of data in the external system you can update the call to do so. (Note: you would have to have the external data api set up to add parameters to the call in order to do this.) Another option would be updating the call based on the user logged in to show different data for different users.

In order to do this you will first need to update the URL on the End Point to call a custom automation script. You will need to update your URL to be replaced with “script:LAST24HOURS” instead of the hard coded date value. An example URL would be “http://externalsystem?data=script:LAST24HOURS”

Once you have the URL constructed you will need to make an Automation Script called LAST24HOURS. The script will need to look like this:

Note: The value that is passed back to the URL is what you set token to.

Now every time the user goes to see the data and the URL is called and the script will run to pass in the value to only show the last 24 hours.

You can pass in any value that you chose by using the automation script. It does not just have to be a date. It can be a value based on the user that is logged in and making the call.

MBOs in Automation Scripts: Reading Single Attributes of an MboSet

In our last post in this series, we talked about how an MboSet is a collection of Mbo objects. This is analogous to a spreadsheet of data representing an MboSet, and a single row within that spreadsheet representing an Mbo. This article will discuss how to read single attributes from an Mbo, or in our spreadsheet analogy, look at data within a single cell within a row.

Once an MboSet has been fetched, data associated with the individual objects it contains can be extracted. Attributes that are associated with character data types, such as ALN, UPPER, and LOWER, can be retrieved by using the getString() method.

The getString() method is a member of both the Mbo and MboSet classes. The Mbo version fetches data from the Mbo object itself; the MboSet version fetches data from the current member of the MboSet.

For example, here moveFirst() is called to set the current member pointer to the first object in the woSet collection. Since woSet was newly restricted from a new setWhere() call, moveFirst() also triggers the retrieval of the record from the server. The getString() method of the woSet object is called and the value of the description attribute of the current member is retrieved.

woSet = mbo.getThisMboSet()
woSet.setWhere("wonum='1100'")
woSet.moveFirst()
woDesc = woSet.getString("DESCRIPTION")

In the next example the moveFirst() call, in addition to the above functions, also returns the work order object which is the new current member of woSet. The getString() method is called through the object wo and the value of the description attribute is retrieved.

woSet = mbo.getThisMboSet()
woSet.setWhere("wonum='1100'")
wo = woSet.moveFirst()
woDesc = wo.getString("DESCRIPTION")

Next, moveFirst() is called to make the first object the current member. A reference to that member is then retrieved by a getMbo() call. The getString() method is then called as before.

woSet = mbo.getThisMboSet()
woSet.setWhere("wonum='1100'")
woSet.moveFirst()
wo = woSet.getMbo()
woDesc = wo.getString("DESCRIPTION")

Note that the result is the same in each of these three examples. Which combination of calls should be used to reach the result depends entirely on what is most convenient in a given situation.

The getString() method may be used on all attributes regardless of their data types.

There are also getDate(), getInt(), getLong(), getByte(), getBoolean(), getFloat() and getDouble() methods, which return the value of an attribute as something other than a string. An attempt to use these methods on an underlying attribute not of the correct type results in an exception being thrown. For example the following does not work because the attribute is not of type DATE or DATETIME.

woDate = woSet.getDate("DESCRIPTION")

A list of the attributes whose values may be retrieved and the objects to which they belong is listed in the MAXATTRIBUTE table in the Maximo database. The OBJECTNAME column of this table corresponds to the table name, while the ATTRIBUTENAME column corresponds to the column name.

For a complete description of all methods available, refer to the Mbo and MboSet JavaDocs on the IBM Asset Management Developer Center.

Our next article will focus on reading multiple attributes from the current record in an MboSet.

Call Maximo Automation Scripts from JSON API

It was not until I read chapter 14 of IBM’s overview of the Maximo JSON API that it occurred to me that we could use the JSON API to launch an automation script in Maximo and then see the results in the return message. We can create our own APIs using the relative simplicity of an automation script and not have to write a single line of Java code. Truly powerful stuff!

The example provided in the article was also useful to me in that I was going through the process of loading data into Maximo when I came across the article. The script used in the article queries Maximo objects and reports back a record count. In my situation this was a very poignant and timely revelation.

Creating the Script

The first step in this process is to create an automation script that we want Maximo to run. I took the script provided in the example and expanded the list of objects to suit my situation. Besides the additional objects I wanted a total count of records that were loaded which is the purpose of the “Get Total Count” section at the end of the script.

Source Code:

importPackage(Packages.psdi.server);
// Create the response object
var resp = {};
// Get the Site ID from the Query Parameters
var site = request.getQueryParam("site");
// Count of Work Orders
var woset = MXServer.getMXServer().getMboSet("WORKORDER", request.getUserInfo());
woset.setQbe("SITEID","="+site);
var woCount = woset.count();
resp.wo_count = woCount;
woset.close();
// Count of Service Requests
var srset = MXServer.getMXServer().getMboSet("SR", request.getUserInfo());
srset.setQbe("siteid","="+site);
var srCount = srset.count();
resp.sr_count = srCount;
srset.close();
// Count of Items
var itmset = MXServer.getMXServer().getMboSet("ITEM", request.getUserInfo());
var itmCount = itmset.count();
resp.item_count = itmCount;
itmset.close();
// Get Total Count
resp.total = woCount+srCount+itmCount;
var responseBody = JSON.stringify(resp);

Navigate to the Automation Script application and select Create and then choose Script.


Since we are going to be launching this script from an HTTP call we just need to create the script and not provide a separate launch point.

Fill out the script Name, Description, Language and the Source Code from above.

Make sure the script is Active and select Create to save the script.

Executing the Script

We need three pieces of information to complete a successful JSON API call:

  • Site ID
    • In this example we are using the demonstration data, so our site is BEDFORD.
  • Username and Password
    • User Name: maxadmin
    • Password: maxadmin
  • URL
    • http://maximo_host/maximo/oslc/script/a3j_recordcount?_lid=maxadmin&_lpwd=maxadmin&site=BEDFORD
    • Note that there are underscores in front of lid and lpwd which might now be obvious from the link formatting above.

Script Results

{
"wo_count": 1331,
"sr_count": 41,
"item_count": 357,
"total": 1729
}