October 30, 2013

Analyze groups to users association

The following SQL query lists all the security groups and the associated users.

SELECT maxgroup.groupname, maxgroup.description, maxuser.userid, maxuser.loginid, maxuser.defsite
FROM maxgroup
JOIN groupuser ON groupuser.groupname=maxgroup.groupname
JOIN maxuser ON maxuser.userid=groupuser.userid
ORDER BY maxgroup.groupname, maxuser.userid;

The best approach to analyze the results is to load them into Excel and create a pivot table.

October 21, 2013

Automatically refresh StartCenter

Users that heavily rely on StartCenters to check for incoming tickets or work orders sometimes ask if the StartCenter can automatically refresh itself.

To accomplish this you can inject a small JavaScript into the StartCenter JSP code.
Backup [SMPDIR]\maximo\applications\maximo\maximouiweb\webmodule\webclient\components\startcenter-options.jsp file and open it with a text editor.
Search for a row like this

<table ...

Add the following code just before that row.

<!-- BEGIN StartCenter automatic refresh -->
<script type="text/javascript">
  setTimeout("location.reload(true)", 60000);
</script>
<!-- END StartCenter automatic refresh -->

The refresh time in the previous script is in milliseconds so 60000 means 1 minute.

Rebuild and redeploy Maximo EAR file.

Now your start center will be automatically refreshed every 2 minutes.

Maximo 7.5 has introduced an exit warning so you may be prompted with a warning like this every minute.


To avoid this you can disable the exit warning as described in this TechNote.

Be careful when deciding how to set the refresh interval because setting it too low may impact overall system performances.

If this technique is not working you could look at this thread.

Enable autorefresh for one user group

A fellow Maximo consultant has applied this technique only to one user group using the following code.

<%@ page import="psdi.webclient.beans.startcntr.*" %>
<% 

try 
{
  SessionContext sessionContext = new SessionContext(wcs);
  StartCenterAppBean startcenter = (StartCenterAppBean)sessionContext.getCurrentApp().getAppBean();
  psdi.mbo.MboRemote mbo = startcenter.getMbo();
  String where = "groupname = '[GROUPNAME]' and userid in (select userid from maxuser where personid = '" + mbo.getUserInfo().getPersonId() + "')";
  psdi.mbo.MboSetRemote groups = mbo.getMboSet("$GROUPUSER", "GROUPUSER", where);
  groups.moveFirst(); 

  if ( !groups.isEmpty() ) 
  {
%>
    <script type="text/javascript"> setTimeout('window.location=window.location' ,10000); </script>
<% 
  } 
} 

catch (Exception e) 
{
  e.printStackTrace(); 
}
%>

The [GROUPNAME] must be replaced with the security group for which you want to enable the start center autorefresh.

Update
JedrekWiehas suggested to modify the script to set the session id in order to avoid duplication os user's sessions.

String refreshUrl = wcs.getMaximoRequestURI()
                  + "?event=loadapp&value=startcntr&"
                  + wcs.getUISessionUrlParameter()
                  + wcs.getCSRFTokenParameter();
%>
<script type="text/javascript"> setTimeout('window.location=window.location' ,10000); </script>
<% 



October 18, 2013

Object Quick Summary

One of the customer that I'm working with is heavily relying on work logs. Unfortunately, the work logs are displayed in a standard table so there is no way of quickly displaying all the logs entered by the users.



The solution I have designed is a QuickSummary tab in the Work Order Tracking application that displays the main attributes of the selected work order together with the historical work logs in one single text box.



The described solution relies on the Rich Text editor control available in TPAE 7.5 but can be easily adapted to a standard multiline text box available in previous version of Maximo.
Here are the configuration steps.


Database Configuration

The first step is to define a virtual (non persistent) attribute that will be used to display the work order summary text.

Open WORKORDER object in Database Configuration and add the following attribute.
  • Attribute: WOSUMMARY
  • Title: Summary
  • Type: CLOB
  • Persistent: false
Save and apply database changes.


Application Designer

The second step is to add a new tab to the WOTRACK application that contains just a large texbox that will display the attribute defined above.

Open WOTRACK application in Application Designer and export application XML. Open the wotrack.xml file with a text editor and add the following lines just before the </tabgroup></clientarea> tags.
Here is how the file should look like (the text in bold is what needs to be added).

  ...
  <tab id="summary" label="QuickSummary">
    <section id="summary_s1">
      <richtexteditor dataattribute="WOSUMMARY" height="400" id="summary_s1_wosummary" plugins="[]" extra_plugins="[]"/>
    </section>
  </tab>
  </tabgroup>
  </clientarea>
  <include id="pageFooter"/>
</page>
...


Script

The last configuration steps is to create the script that will fill the WOSUMMARY field and attach it to the rich text control. The sample script retrieves all the necessary information from the work order and builds an HTML string that it is used to set the field value.

Goto System Configuration > Platform Configuration > Automation Scripts.
Select Action > Create Script and enter this information:
  • Script: WOSUMMARY
  • Description: Init Workorder summary
  • Script language: jython
  • Source code: paste the code below

from java.lang import StringBuilder
from psdi.mbo import MboConstants


val = StringBuilder()

# retrieve attributes and writes them in HTML format in the buffer
val.append("ID: " + mbo.getString("WONUM") + "<br>")
val.append("Description: " + mbo.getString("DESCRIPTION") + "<br>")
val.append("Location: " + mbo.getString("LOCATION") + "<br>")
val.append("Asset: " + mbo.getString("ASSETNUM") + "<br><br>") 

# retrieve work log records and writes them in the buffer
worklogSet = mbo.getMboSet("MODIFYWORKLOG")

val.append("<hr>")
val.append("<h2>WORK LOG</h2>")
val.append("<hr>")

currMbo=worklogSet.moveFirst()

while currMbo is not None:
    
        val.append("<h3>" + currMbo.getString("DESCRIPTION") + "</h3><br>")
        val.append(currMbo.getString("DESCRIPTION_LONGDESCRIPTION") + "<br>")
        val.append("<hr>")
        currMbo=worklogSet.moveNext()
    

#  sets the formatted string in the attributes value
mbo.getMboValue("WOSUMMARY").setValue(val.toString(), MboConstants.NOACCESSCHECK | MboConstants.NOVALIDATION)

Click on the Create button.
Now you have to attach the script to the WOSUMMARY field. Select Action > Create Script with Object Launch Point and enter the following information:

  • Launch Point: WOSUMMARY
  • Descriptions: Fills the workorder summary
  • Object: WORKORDER
  • Active: True
  • Events: Initialize
  • Script: WOSUMMARY
Accept defaults on the other dialogs and create the launch point.

Now you are done. Open a sample Workorder and go the the QuickSummary tab and look at the results.


Further improvements

There are many ways to improve the QuickSummary feature.

Obviously you can extend the example to include your own fields and formatting to better fit your needs.

Look at how HTML tags have been used to format the output in the script. This means that you can improve the formatting of the summary to enhance readability.


Thanks to my colleague Dirk Hillmer who has helped me with TPAE scripting.

October 17, 2013

Implementing a Smart Search feature

Using Maximo search functionality can be sometimes complex or even frustrating.
Several customers have asked me if there is a simple Google-like functionality to search for all the objects that contains a specific set of words.
This article describes how to implement such feature in the Work Order Tracking application.

The solution described in this tutorial allows to display a simple search dialog when the user clicks on a specific button on the toolbar. When the user presses the 'Search' button the application will list the work orders containing all the keywords in any of the following fields: description, long description, location, asset, work logs.
Note that this is not achievable using the Advanced Search dialog since this will search with an AND logic instead of an OR logic implemented here.

In this screenshot you can see the custom search button and the very simple SmartSearch dialog.




The basic theory of how to create a custom dialog box to ask for user input is described in details in this article.


Create the custom Dialog Box

First of all we need to create the SmartSearch dialog box.
  1. Launch the Application Designer and open the WOTRACK application.
  2. Export the XML application definition to a local file.
  3. Open the exported file with a text editor.
  4. Scroll all the way down to the bottom of the file.
  5. Just before the </presentation> end tag, insert the XML code below.
  6. Save your changes.
  7. Go back to the Application Designer application.
  8. On the List tab, click on the Import Application Definition toolbar button to import your modified file.

Here is the dialog box definition.

<dialog beanclass="mxdev.webclient.beans.workorder.SmartSearchAppBean" id="ssearch" label="SmartSearch">
  <section border="false" id="ssearch_s1" mboname="smartsearch">
    <textbox dataattribute="searchtext" label="Text" id="ssearch_s1_1"/>
    <checkbox dataattribute="searchhist" label="Include historical records" id="ssearch_s1_2"/>
    <blankline id="ssearch_s1_3"/>
  </section>
  <buttongroup id="ssearch_buttons_1">
    <pushbutton default="true" id="ssearch_btn1" label="Search" mxevent="dialogok"/>
    <pushbutton id="ssearch_btn2" label="Cancel" mxevent="dialogcancel"/>
  </buttongroup>
</dialog>



Define the sigoption

Follow these steps to define the sigoption for the custom dialog box.
    1. Launch the Application Designer and open the WOTRACK application.
    2. On the Select Action menu, click on Add/Modify Signature Options.
    3. Click on New Row to add a new row:
      Option: SSEARCH
      Description: SmartSearch
    4. Exit out the Application Designer application.
    5. Launch the Security Groups application and open "EVERYONE" group.
    6. Go to the Applications tab.
    7. Search fo Work Order Tracking application and then for the 'SmartSearch' the new signature option. Grant access to it and save your changes.
    8. Done.


    Create toolbar button


    Follow these steps to create a custom toolbar button to launch your dialog box.
    1. Launch the Application Designer application.
    2. Retrieve the application which you modified on step 2.
    3. On the Select Action menu, click on Add/Modify Toolbar Menu.
    4. Click on New Row to add a new row. Use the following values:
      Element Type = OPTION
      Key Value = SSEARCH
      Position = 80
      Subposition = 0
      Image = find.gif
      Visible = Yes
      Tabs = LIST
    5. Click OK..
    6. Logout and login back.

    Checkpoint
    Launch the Work Order Tracking application and check that the new button and that by clicking on it the new SmartSearch dialog is displayed. Note that at this point you will still see an 'Invalid Binding' error.


    Create SMARTSEARCH object

    Follow these steps to create a non persistent object to support the application bean.
    • Launch Database Designer application.
    • Create an new object and enter the following information
      Object: SMARTSEARCH
      Description: Non persistent bean for SmartSearch
      Persistent: False
    • Save the record and go the the Attributes tab.
    • Create the following attribute:
      Attribute: SEARCHTEXT
      Title: Search text
      Type: ALN
      Length: 100
    • Create the following attribute:
      Attribute: SEARCHHIST
      Title: Include historical records
      Type: YORN
      Default: 0
    • Save and apply database changes


    SmartSearch AppBean class

    Follow these steps to create and deploy the AppBean class.

    1. Download SmartSearchAppBean.java file.
    2. Open your development environment and copy SmartSearchAppBean.java into  mxdev.webclient.beans.workorder package.
    3. Compile and copy SmartSearchAppBean.class file in
      [SMPDIR]\applications\maximo\maximouiweb\webmodule\WEB-INF\classes\mxdev\webclient\beans\workorder
    4. Re-build and deploy Maximo EAR file.

    You can easily modify the set of fields in which the text is searched by modifying how the where clause string is built.


    References

    Custom Dialog Box tutorial (from Daniel NG)
    Call Java method on action menu or toolbar button click

    October 16, 2013

    Display a summary of an object

    NOTE: This techniques requires Java programming. A similar article describes how to achieve the same results with scripting.

    One of the customer that I'm working with is heavily relying on work logs. Unfortunately, the work logs are displayed in a standard table so there is no way of quickly displaying all the logs entered by the users.



    The solution I have designed is a Summary tab in the Work Order Tracking application that displays the main attributes of the selected work order together with the historical work logs in one single text box.



    The described solution relies on the Rich Text editor control available in TPAE 7.5 but can be easily adapted to a standard multiline text box available in previous version of Maximo.
    Here are the configuration steps.


    Database Configuration

    The first step is to define a virtual (non persistent) attribute that will be used to display the work order summary text.

    Open WORKORDER object in Database Configuration and add the following attribute.
    • Attribute: WOSUMMARY
    • Title: Summary
    • Type: CLOB
    • Class: mxdev.app.wo.FldWoSummary
    • Persistent: false
    Save and apply database changes.


    Application Designer

    The second step is to add a new tab to the WOTRACK application that contains just a large texbox that will display the attribute defined above.

    Open WOTRACK application in Application Designer and export application XML. Open the wotrack.xml file with a text editor and add the following lines just before the </tabgroup></clientarea> tags.
    Here is how the file should look like (the text in bold is what needs to be added).

      ...
      <tab id="summary" label="Summary">
        <section id="summary_s1">
          <richtexteditor dataattribute="WOSUMMARY" height="400" id="summary_s1_wosummary" plugins="[]" extra_plugins="[]"/>
        </section>
      </tab>
      </tabgroup>
      </clientarea>
      <include id="pageFooter"/>
    </page>
    ...


    Java class

    The last configuration steps is to write the Java class that has been attached to the WORKORDER.WOSUMMARY field. This piece of code will retrieve all the necessary information from the work order and build an HTML string that will be assigned to the field string value.

    Open your development environment and create a mxdev.app.wo.FldWoSummary class like this.

    package mxdev.app.wo;
    
    import java.rmi.RemoteException;
    
    import psdi.mbo.Mbo;
    import psdi.mbo.MboRemote;
    import psdi.mbo.MboSetRemote;
    import psdi.mbo.MboValue;
    import psdi.mbo.MboValueAdapter;
    import psdi.util.MXException;
    
    public class FldWoSummary extends MboValueAdapter
    {
      public FldWoSummary(MboValue mbv) throws MXException
      {
        super(mbv);
      }
    
      public void initValue() throws MXException, RemoteException
      {
        super.initValue();
    
        MboRemote wo = this.getMboValue().getMbo();
        StringBuilder val = new StringBuilder(); 
    
        // retrieve attributes and writes them in HTML format in the buffer
        val.append("ID: " + wo.getString("WONUM") + "<br>");
        val.append("Description: " + wo.getString("DESCRIPTION") + "<br>");
        val.append("Location: " + wo.getString("LOCATION") + "<br>");
        val.append("Asset: " + wo.getString("ASSETNUM") + "<br><br>");
    
        // retrieve work log records and writes them in the buffer
        MboSetRemote worklogSet = wo.getMboSet("MODIFYWORKLOG");
    
        val.append("<hr>");
        val.append("<h2>WORK LOG</h2>");
        val.append("<hr>");
      
        for(MboRemote currMbo=worklogSet.moveFirst(); currMbo!=null; currMbo=worklogSet.moveNext())
        {
          val.append("<h3>" + currMbo.getString("DESCRIPTION") + "</h3><br>");
          val.append(currMbo.getString("DESCRIPTION_LONGDESCRIPTION") + "<br>");
          val.append("<hr>");
        }
    
        // sets the formatted string in the attributes value
        getMboValue().setValue(val.toString(), Mbo.NOACCESSCHECK | Mbo.NOVALIDATION);
      }
    
    }

    Deploy the java class in Maximo EAR file.

    You can see how HTML tags have been used to format the output.
    Obviously you can extend the example to include your own fields and formatting to better fit your needs.

    October 15, 2013

    How to implement stateful MBOs

    This entry is part of the Maximo Java Development series.

    Many objects in Maximo has a STATUS attribute that can be changed using the status icon or the change status menu option. The STATUS attribute is always associated with a synonym domain that has the valid statuses for the object. Examples of these domains are:
    • POSTATUS for the PO (Purchase Order) object
    • WOSTATUS for the WORKORDER object


    The objects with changes of status must inherit from the StatefulMbo class which provides the basic functionality to change the status. For example the WORKORDER object has the following class hierarchy:
    • Mbo > StatefulMbo > WORKORDER

    The synonym domain is associated with the STATUS attribute, and this domain has an internal and external value. The value stored in the database and displayed on the screen is the external value for an attribute with a synonym domain.



    The most important methods available in the StatefulMbo class are:
    • canChangeStatus(String changeToStatus, long accessModifier)
      Identifies whether the status of the object be changed to the specified value.
      If status change is not allowed, an exception is thrown.
    • changeStatus (String status, Date date, String memo)
      Changes the status of the object.
      This method compares the requested status and the current status by calling canChangeStatus before the change is performed. If the status change is allowed, the status change is performed and a status history record is written. If the status change is not allowed, an exception is thrown.
    • getInternalStatus()
      Returns the Internal value for the current status of the object
    • getStatusListName()
      Return the name of the domain associated with the status attribute.

    Passing the NOACCESSCHECK flag in the accessModifier parameter can be used to indicate that the status can be changed even if it is invalid.
    Two similar methods that can be used to manipulate internal status values are: canChangeMaxStatus and changeMaxStatus.

    When the user selects the 'Change Status' option, the framework displays a dialog box that lists the valid statuses to which the object can change.



    The list is the result of calls to the canChangeStatus method for each of the possible statuses from the synonym domain. If the canChangeStatus method throws an exception, the status is not listed.
    When the user selects the new status, the framework calls the changeStatus method that first call the the canChangeStatus method (again) to validate the change and then update the status and status history.

    October 13, 2013

    MBOs attribute current, previous, initial values

    When implementing business logic in Java code it may be useful to compare the value of an attribute to it's previous or initial value.
    This is particularly useful when overriding MboValueAdapter.action() method to validate the new value of an attribute as soon as it changes.

    Look at how the three values are retrieved in the following example.

    @Override
    public void action() throws MXException, RemoteException
    {
      super.action();
    
      String currVal = getMboValue().getString();
      String oldVal = getMboValue().getPreviousValue().asString();
      String initialVal = getMboValue().getInitialValue().asString();
    
      if (!currVal.equals(oldVal))
      {
        ...
      }
    }

    October 6, 2013

    System requirements for IBM Maximo and SmartCloud Control Desk

    Sometimes is a bit hard to find an exact list of system requirements and prerequisites for IBM Maximo and SmartCloud Control Desk (SCCD) products.

    If you want to know what are the supported operating systems and middleware versions here are the most important sources of information: