Wednesday 5 February 2020

Script to run report and save as attachment in Maximo

One of my customers asked me to take a kind of snapshot of work orders at a specific point of the workflow to keep track of special approvals and exact situation of the approved record.
I decided to implement such requirement through the implementation of an automation script to generate a BIRT report automatically and attach it the work order. I my scenario I have triggered it from the workflow but it can be also triggered from an escalation.
#-------------------------------------------------------------------------------
#
# Script: WOREPORTGENSAVE
#
# Launch Point: WORKORDER - Action
#
# Generate a BIRT report and save it as an attachment.
#
#-------------------------------------------------------------------------------

from psdi.mbo import Mbo, MboConstants
from psdi.util.logging import MXLoggerFactory
from psdi.server import MXServer

from com.ibm.tivoli.maximo.report.birt.admin import ReportAdminServiceRemote
from com.ibm.tivoli.maximo.report.birt.runtime import ReportParameterData

from java.io import FileOutputStream

#-------------------------------------------------------------------------------

logger = MXLoggerFactory.getLogger("maximo.dev")

reportName = "wodetail.rptdesign"    # name of the report to be lauched
appName = "WOTRACK"                  # application
reportFolder = "Attachments"         # folder where the report will be stored

#-------------------------------------------------------------------------------

logger.debug("Entering WOREPORTGENSAVE")

wonum = mbo.getString("WONUM")

logger.debug("Retrieving destination file and folder")

doctypesMboSet = MXServer.getMXServer().getMboSet('DOCTYPES', mbo.getUserInfo())
doctypesMboSet.setWhere("DOCTYPE='" + reportFolder + "'")

outputFilePath=doctypesMboSet.getMbo(0).getString('DEFAULTFILEPATH')

logger.debug("Output folder: " + outputFilePath)

outputFileName = wonum + "-details.pdf"
outputFile = outputFilePath + "/" + outputFileName

logger.debug("Output file: " + outputFile)
logger.debug("Generating report" + reportName)

# get the handler to BIRT report engine
reportAdminService = MXServer.getMXServer().lookup("BIRTREPORT")

# pass WONUM as report parameter
parameterData = ReportParameterData()
parameterData.addParameter("where", "(WORKORDER.wonum='"+wonum+"')")

reportBytes = reportAdminService.runReport(MXServer.getMXServer().getSystemUserInfo(), reportName, appName, parameterData, outputFileName, ReportAdminServiceRemote.OUTPUT_FORMAT_PDF)

# writes the binary data to the output file
fos = FileOutputStream(outputFile)
fos.write(reportBytes)
fos.close()

logger.debug("Creating DOCLINKS record")

doclinksMboSet = mbo.getMboSet("DOCLINKS")

doclinksMbo = doclinksMboSet.add()

doclinksMbo.setValue("URLTYPE", "FILE")
doclinksMbo.setValue("URLNAME", outputFile)
doclinksMbo.setValue("NEWURLNAME", outputFile)
doclinksMbo.setValue("DOCTYPE", reportFolder)
doclinksMbo.setValue("ADDINFO", True)
doclinksMbo.setValue("DESCRIPTION", "Test Results")

How to correctly define a Table Domain

Have you ever wondered what’s the difference between ‘Validation Where Clause’ and ‘List Where Clause’ in the Maximo Table Domain definition dialog?
I believe this is one of the most common misused features in Maximo. I have see so many times tables domains defined in the wrong way. Let me show a good example of a built-in table domain.
The ACTIVEUSER domain is used to allow the selection of an active user in the Cron Tasks application. Here are the domain details.
  • Domain: ACTIVEUSER
  • Object: MAXUSER
  • Validation Where Clause: userid=:runasuserid
  • List Where Clause: status in (select value from synonymdomain where domainid=’MAXUSERSTATUS’ and maxvalue=’ACTIVE’)
  • Error Message Group: signature
  • Error Message Key: NotActiveUser
Lets now analyze two key characteristics of this table domain, the validation where clause and the error message.

Error Message

If we open the Cron Tasks application and try to type some wrong value in the ‘Run As User’ field we will get a specific error: Run as User XXXYYYZZZ is not an active User
Removing the error message group/key from the domain definition we get the generic domain validation error: The value XXXYYYZZZ is not valid for Run as User
The error message group/key properties a Table Domain definition allow to display a more specific error message to the user when the entered value is not valid.
To further improve the error message you can use the {1} param to display the invalid value and the {0} param to display the field name the domain is tied to.

Validation Where Clause

The userid=:runasuserid statement in the Validation Where Clause ensures that the value entered in the field (CRONTASKINSTANCE.RUNASUSERID) matches a value in the data source table (MAXUSER.USERID).
To understand the reason why the Validation Where Clause is so important we can simply try to remove it and see what happens. After having modified the domain we can enter any value in the Run as User field !!!
This means that:
The Validation Where Clause must be always set to validate the entered value against the target table domain.
In our example userid is the attribute of the MAXUSER table that the domain is based on. Instead :runasuserid is the attribute of the CRONTASKINSTANCE table that you want to validate.

UI Conditional Expression Manager in Maximo and IBM Control Desk

In Assets application


Asset 12345 is an IT asset. It will have the following features:

  • Label of Asset field is blue and says ‘IT Asset’
  • Safety Tab is displayed
  • Change Status is enabled
  • The ‘Usage’ field has 7 values in the lookup
  • Serial Number is a required attribute


Asset 12346 is a Facilities asset. It will have the following features:

  • Standard label
  • Safety Tab is not displayed
  • Change Status disabled
  • The Usage field has 2 values in the lookup
  • Serial Number is not required


Configuration


Conditional Expression Manager


  • Condition – TYPEIT
    • Description - Asset type is IT
    • Type – Expression
    • Expression - :assettype = ‘IT’

Application Designer




New SIGOPTIONS for Asset application

  • SAFTAB with the description Safety Tab



New Control Properties

  • Safety Tab 
    • SIGOPTION of SAFTAB
  • Asset field

·         SIGOPTION of READ

·         Conditional Properties

·         Group = CUIGRP

·         Condition = TYPEIT

·         two true properties

1.    Property – label

·         Property Value – IT Asset

2.    Property – labelcss

·         Property value – txtblue



Security Groups


·         CUIGRP Group

o   Applications Tab

§  Assets Application

·         Change Status granted in condition TYPEIT

·         Safety Tab granted in condition TYPEIT

o   Data Restrictions Tab

§  Attribute Restriction

·         Object:= Asset

·         Attribute = SERIALNUM

·         Type = REQUIRED

·         Condition - TYPEIT


Domains



  • USAGE domain
    • BaseWS, StandardWS, SalesWS, Server and ProWS  values have TYPEIT condition
    • FLEET and MOTORPOOL values have no condition







Maximo Scripting – CanDelete/CanAdd Object Launch Point

We can control whether we can add or delete a Mbo using scripting Object Launchpoint “Allow Object Deletion” and “Allow Object Creation”.

Can Add

This is an Object Launch Point – “Allow Object Creation” where you can control whether you can add a new Mbo, given the current state of the system. This point pairs up with the canAdd callback that we have in the MboSet framework.
Lets take a use case where we want to validate that a POLINE can be added to a PO only when the PO has the vendor information set.
The script code to do that is shown below:
if mboset.getOwner() is not None and mboset.getOwner().getName()=="PO" and mboset.getOwner().isNull("vendor"): 
   service.error("po", "novendor_noline") 
Note that here, there is no implicit variable called “mbo” as the launch point is invoked before the Mbo is created. At that point all you have is the MboSet (implicit variable “mboset”) for the POLINE.
If you are wondering why this cannot be done using the init Object launch point, the answer is that its little too late. At that point the Mbo has already been created and added to the set. Rejecting it at the point would have not helped.

Can Delete

Similar to the “Can Add”, this Object Launch Point helps validate whether a Mbo can be deleted or not. Going with the POLINE object, say we want to enforce a validation that the line should not be deleted, if the PO is of priority 1.
if mbo.getOwner() is not None and mbo.getOwner().getName()=="PO" and !mbo.getOwner().isNull("priority") and mbo.getOwner().getInt("priority")==1: 
   service.error("po","nolinedelete_forpriority1") 
Note that in this case, the mbo is already there (which we are trying to validate for deletion) and hence we can leverage implicit variable mbo to do the validation.

IBM Readme for IBM Maximo Asset Management 7.6.1.3 Fix Pack

  Fix Readme Abstract This fix pack updates IBM® Maximo® Asset Management version 7.6.1, 7.6.1.1, and 7.6.1.2 Content IBM Maximo Asset Manag...