IBM Support

Conditional Restrictions for Classification Specifications (Attributes)

Technical Blog Post


Abstract

Conditional Restrictions for Classification Specifications (Attributes)

Body

IBM Maximo and Control Desk products have classifications in order to categorize records such as items, assets, locations, work orders, tickets etc. Classifications are not only useful for easily identifying, restricting or filtering records, they also bring in specifications functionality. Specification attributes are extra fields defined for a classification used with one or more Maximo objects. In spite of adding new fields to main database tables, related specification records are added to the specification tables only for the classified records. Hence, using classification specifications prevents redundancy and excessive number of columns for main database tables such as item, asset, locations, workorder, ticket, etc. which improves both database and overall system performance. Specifications are usually located in the Specifications tab of the original Maximo applications.

image

Figure 1 - Specifications Tab on Assets Application

 

Regular fields (attributes) can be conditionally or deterministically configured as required or readonly in various ways. On the contrary, specification attributes can be statically set as required fields. The customization I will present adds an interface to Maximo (or Control Desk) product to handle mandatoriness of specification attributes dynamically by using the existing conditional expression structure. It also adds another feature to conditionally validate the specification attributes and raise error messages for invalid input values. One can implement a similar feature to conditionally make the specification attributes readonly.

Note: On latter sections, there exists some Turkish labels since I implemented this solution in Turkish only. However, I have noted English translations for some important labels for comprehension.

 

1. Adding a custom business object to hold restriction definitions

 

First of all, we add a custom object (SPECCONDITIONS) via Database Configuration application which will be used to hold the restriction conditions for mandatoriness and validation of specification attributes. The attribute list for the custom object can be seen In Table 1 below.

 

Field ID

Description

Type

Length

Required?

Usage

ACTIVE

Is Active?

YORN

1

Y

Field default value is “1”. It is set to “0” for inactive conditions.

ASSETATTRID

Asset Attribute ID

UPPER

8

Y

Same as ASSETATTRIBUTE.ASSETATTRID field. It defines the asset attribute of specification.

CHANGEBY

Change By

UPPER

30

Y

The last person who changed the record.

CHANGEDATE

Change Date

DATETIME

10

Y

Date and time of the last change on the record.

CLASSSTRUCTUREID

Classification Identifier

UPPER

20

N

Same as the CLASSSPEC.CLASSSTRUCTUREID field. It defines the classification of the specification.

CREATEBY

Create By

UPPER

30

Y

The person who created the record.

CREATEDATE

Create Date

DATETIME

10

Y

Creation date and time of the record.

DESCRIPTION

Description

ALN

50

N

The description or comment for the specification condition record.

SPECCONDITIONSID

Specification Conditions Unique Identifier

BIGINT

19

Y

System set unique identifier.

MSGID

Message ID

ALN

20

N

Same as the MAXMESSAGES.MSGID field. It defines the message for invalid input conditions.

OBJECTNAME

Object Name

UPPER

30

Y

Same as the CLASSUSEWITH.OBJECTVALUE field. It defines the object used for classification of the specification attribute.

ORGID

Organization ID

UPPER

8

N

Same as the CLASSSTRUCTURE.ORGID field. It defines the organization of the classification.

REQCONDITION

Required Condition

UPPER

12

N

Same as the CONDITION.CONDITIONNUM field. The usual Maximo condition structure is used to check if the specification attribute is required or not.

REQISMAIN

Is Required Condition for Main Record?

YORN

1

Y

The value is “1” if the required condition is for the main record (item, asset, locations, workorder, ticket, etc.), the value is “0” if the required condition is for the specification record (itemspec, assetspec, locationspec, workorderspec, ticketspec, etc.)

SITEID

Site ID

UPPER

8

N

Same as the CLASSSTRUCTURE.SITEID field. It defines the site of the classification.

VALCONDITION

Validation Condition

UPPER

12

N

Same as the CONDITION.CONDITIONNUM field. The usual Maximo condition structure is used to check if the specification attribute is valid or not.

VALISMAIN

Is Validation Condition for Main Record?

YORN

1

Y

The value is “1” if the validation condition is for the main record (item, asset, locations, workorder, ticket, etc.), the value is “0” if the validation condition is for the specification record (itemspec, assetspec, locationspec, workorderspec, ticketspec, etc.)

Table 1 - Attribute List of Specification Conditions Object

 

After that we turn on Admin mode, apply Database configurations and turn Admin mode off. Then we add relationships, seen in Table 2 below, to/from our new custom object, which will be used in the Automation Scripts and applications to access remote objects.

 

Relation Name

Parent Object

Child Object

Where Clause

ASSETATTRIBUTE

SPECCONDITIONS

ASSETATTRIBUTE

assetattrid = :assetattrid

CLASSSTRUCTURE

SPECCONDITIONS

CLASSSTRUCTURE

classstructureid = :classstructureid

MAXMESSAGES

SPECCONDITIONS

MAXMESSAGES

msgid = :msgid

MAXOBJECT

SPECCONDITIONS

MAXOBJECT

objectname = :objectname

REQCONDITION

SPECCONDITIONS

CONDITION

conditionnum = :reqcondition

VALCONDITION

SPECCONDITIONS

CONDITION

conditionnum = :valcondition

SPECCONDITION

ASSETSPEC

SPECCONDITIONS

active = 1 and objectname = 'ASSET' and assetattrid = :assetattrid and nvl(classstructureid, :classstructureid) = :classstructureid

SPECCONDITION

ITEMSPEC

SPECCONDITIONS

active = 1 and objectname = 'ITEM' and assetattrid = :assetattrid and nvl(classstructureid, :classstructureid) = :classstructureid

SPECCONDITION

LOCATIONSPEC

SPECCONDITIONS

active = 1 and objectname = 'LOCATIONS' and assetattrid = :assetattrid and nvl(classstructureid, :classstructureid) = :classstructureid

SPECCONDITIONS

MAXGROUP

SPECCONDITIONS

1 = 1

Table 2 - Relationship Definitions related to Specification Conditions

 

With the relations defined our custom specification restrictions table is ready to use. In the next steps we will implement the user interface customizations and write the custom Automation scripts.

 

 

2. Adding Specification Restrictions tab to Security Groups application to view and edit specification restrictions

 

In this section, we will add the user interface customization to Security Groups application. We export ”securgroup.xml” from Application Designer application and take a backup of the xml and then search for the following tab group in the xml:

  <tabgroup id="datarest_tabgroup">  

We add our custom tab to the data restrictions tab group. The following xml piece should be copied into the tabgroup tags:

  <tab id="datarest_spec_tab" label="Ek Alan Kısıtlamaları" sigoption="SPECREST">      <table id="datarest_spec_table" label="Ek Alanlar" orderby="objectname, assetattrid" relationship="SPECCONDITIONS">          <tablebody displayrowsperpage="10" filterable="true" id="datarest_spec_table_tablebody">              <tablecol filterable="false" id="datarest_spec_table_tablebody_1" mxevent="toggledetailstate" mxevent_desc="Ayrıntı Göster" sortable="false" type="event"/>              <tablecol dataattribute="objectname" id="datarest_spec_table_tablebody_2" lookup="valuelist"/>              <tablecol dataattribute="assetattrid" id="datarest_spec_table_tablebody_4" lookup="assetattribute"/>              <tablecol dataattribute="description" id="datarest_spec_table_tablebody_3"/>              <tablecol dataattribute="classstructureid" id="datarest_spec_table_tablebody_2a" lookup="classification"/>              <tablecol dataattribute="reqismain" id="datarest_spec_table_tablebody_9"/>              <tablecol applink="condexpmgr" dataattribute="reqcondition" id="datarest_spec_table_tablebody_5" lookup="conditionexp" menutype="normal"/>              <tablecol dataattribute="valismain" id="datarest_spec_table_tablebody_10"/>              <tablecol applink="condexpmgr" dataattribute="valcondition" id="datarest_spec_table_tablebody_11" lookup="conditionexp" menutype="normal"/>              <tablecol dataattribute="active" id="datarest_spec_table_tablebody_8"/>              <tablecol filterable="false" id="datarest_spec_table_tablebody_7" mxevent="toggledeleterow" mxevent_desc="Satırı Silmek İçin İşaretle" mxevent_icon="btn_garbage.gif" sortable="false" type="event"/>          </tablebody>          <tabledetails id="datarest_spec_table_tabledetails_1">              <section id="datarest_spec_table_tabledetails_grid30" label="Ayrıntılar">                  <sectionrow id="datarest_spec_table_tabledetails_r0">                      <sectioncol id="datarest_spec_table_tabledetails_r0_c1">                          <section id="datarest_spec_table_tabledetails_grid30a0">                              <textbox dataattribute="description" id="datarest_spec_table_tabledetailsrow1_3"/>                              <multiparttextbox dataattribute="objectname" descdataattribute="maxobject.description" descinputmode="readonly" id="datarest_spec_table_tabledetailsrow1_1" lookup="valuelist"/>                              <multiparttextbox dataattribute="assetattrid" descdataattribute="assetattribute.description" descinputmode="readonly" id="datarest_spec_table_tabledetailsrow1_1a" lookup="ATTRIBUTENAME"/>                              <multiparttextbox dataattribute="classstructureid" descdataattribute="classstructure.description" descinputmode="readonly" id="datarest_spec_table_tabledetailsrow1_2" lookup="classification"/>                          </section>                      </sectioncol>                      <sectioncol id="datarest_spec_table_tabledetails_r1_c1">                          <section id="datarest_spec_table_tabledetails_grid30a">                              <checkbox dataattribute="active" id="datarest_spec_table_tabledetailsrow1_9"/>                              <textbox dataattribute="siteid" id="datarest_spec_table_tabledetailsrow1_10" lookup="site"/>                              <textbox dataattribute="orgid" id="datarest_spec_table_tabledetailsrow1_11" lookup="organization"/>                          </section>                      </sectioncol>                  </sectionrow>              </section>              <section id="datarest_spec_table_tabledetails_grid31">                  <sectionrow id="datarest_spec_table_tabledetails_r1">                      <sectioncol id="datarest_spec_table_tabledetails_r1_c2">                          <section id="datarest_spec_table_tabledetails_grid30b">                              <checkbox dataattribute="reqismain" id="datarest_spec_table_tabledetailsrow1_22"/>                              <multiparttextbox applink="condexpmgr" dataattribute="reqcondition" descdataattribute="reqcondition.description" descinputmode="readonly" id="datarest_spec_table_tabledetailsrow1_4" lookup="conditionexp" menutype="normal"/>                              <textbox dataattribute="reqcondition.classname" id="datarest_spec_table_tabledetailsrow1_6" inputmode="readonly"/>                              <multilinetextbox columns="35" dataattribute="reqcondition.expression" id="datarest_spec_table_tabledetailsrow1_5" inputmode="readonly" rows="4"/>                          </section>                      </sectioncol>                      <sectioncol id="datarest_spec_table_tabledetails_r1_d2">                          <section id="datarest_spec_table_tabledetails_grid30c">                              <checkbox dataattribute="valismain" id="datarest_spec_table_tabledetailsrow2_22"/>                              <multiparttextbox applink="condexpmgr" dataattribute="valcondition" descdataattribute="VALCONDITION.DESCRIPTION" descinputmode="readonly" id="datarest_spec_table_tabledetailsrow2_4" lookup="conditionexp" menutype="normal"/>                              <textbox dataattribute="valcondition.classname" id="datarest_spec_table_tabledetailsrow2_6" inputmode="readonly"/>                              <multilinetextbox columns="35" dataattribute="valcondition.expression" id="datarest_spec_table_tabledetailsrow2_5" inputmode="readonly" rows="4"/>                              <multiparttextbox dataattribute="MSGID" descdataattribute="MAXMESSAGES.VALUE" descinputmode="readonly" id="1467819877346" lookup="maxmessages"/>                          </section>                      </sectioncol>                  </sectionrow>              </section>          </tabledetails>          <buttongroup id="datarest_spec_table_2">              <pushbutton default="true" id="datarest_spec_table_2_1" label="Yeni Satır" mxevent="addrow"/>          </buttongroup>      </table>  </tab>    

After importing “securgroup.xml” back into the system, we can edit the user interface further (change labels or give input modes etc.) using the Application Designer canvas.

  sigoption="SPECREST"

The signature option above is used for making this tab conditionally visible to a specific security group.

 

The resulting user interface customization can be viewed in Figure 2 below.

image

 

Figure 2 - Specification Restrictions tab with details section in Security Groups application

 

 

3. Implementation of Automation Scripts in order to process the conditions defined for specification restrictions


The creation of custom objects and application modifications were for defining and viewing the specification restrictions. After that, we need to add the logic to the system which will process the conditions defined in specification restrictions. Here, we implement two Automation Scripts for required and validation conditions respectively:

 

3.1 Automation Script for Specification Required Conditions

We create the following script from Automation Scripts application.

  # SPECREQ.py - Automation Script for Specification Required Conditions     from psdi.mbo import Mbo, MboRemote, MboSet, MboSetRemote  from psdi.common.condition import MaxCondition  from psdi.server import MXServer  from java.util import Arrays, List    # function to get the title of the specification attribute  def getAttributeTitle(spec):      if(spec.getMboSet("ASSETATTRIBUTE").isEmpty()):          return spec.getString("ASSETATTRID")      return spec.getString("ASSETATTRIBUTE.DESCRIPTION")    # function to get the column name of the specification attribute: ALNVALUE, TABLEVALUE, NUMVALUE  def getAttributeName(spec):      types = {"ALN": "ALNVALUE", "TABLE": "TABLEVALUE", "NUMERIC": "NUMVALUE"}      if(spec.getMboSet("ASSETATTRIBUTE").isEmpty()):          return None      datatype = spec.getString("ASSETATTRIBUTE.DATATYPE")      return types[datatype]    # function to check the required specification condition  # returns True when the required criteria is met but the attribute is blank   def specRequired(cond, spec):      if(cond.isNull("REQCONDITION")):          return False      conditionNum = cond.getString("REQCONDITION")      condition = MXServer.getMXServer().getConditionCache().get(conditionNum)      if(condition == None):          return False      conditionMbo = mbo if cond.getBoolean("REQISMAIN") else spec      return condition.evaluate(conditionMbo, False) and (spec.isNull(getAttributeName(spec)) or spec.getString(getAttributeName(spec)) == "")    # main code: fetching the relevant specification record and processing the "specRequired" function   # throws an error message with the list of the blank and required attribute titles if there is any  canSave = True  msgparams = ''    relationName = mboname + "SPECCLASS"  titles = {"WORKORDER": "Is Emri", "ASSET": "Demirbas", "LOCATION": "Konum", "ITEM": "Parca"}  title = titles[mboname] if mboname in ('WORKORDER', 'ASSET', 'LOCATION', 'ITEM') else ''    specSet = mbo.getMboSet(relationName)    if(not specSet.isEmpty()):      cnt = specSet.count()      for i in range(cnt):          spec = specSet.getMbo(i)          if(spec.getMboSet("SPECCONDITION").isEmpty()):              continue          cond = spec.getMboSet("SPECCONDITION").getMbo(0)          if(specRequired(cond, spec)):              canSave = False               msgparams = msgparams + '\n' + getAttributeTitle(spec)        if(not canSave):          errorkey = 'requiredMsg'          errorgroup = 'errorMsg'          params = [title, msgparams]  

This script will work when the user tries to save the main record, i.e. item, asset, location, work order, ticket etc. After that, we create the associated object level launchpoints for objects ITEM, ASSET, LOCATIONS, WORKORDER etc. with BEFORE SAVE events having ADD or UPDATE options.

 

3.2 Automation Script for Specification Validation Conditions

We create the following script from Automation Scripts application.

  # SPECVAL.py - Automation Script for Specification Validation Conditions     from psdi.mbo import Mbo, MboRemote, MboSet, MboSetRemote  from psdi.common.condition import MaxCondition  from psdi.server import MXServer  from java.util import Arrays, List  from java.lang import System    # function to check the specification conditions for validation  # returns False when the validation criteria is not met   def specValid(cond, spec):      if(mbovalue is None):          return True      if(cond.isNull("VALCONDITION")):          return True      conditionNum = cond.getString("VALCONDITION")      condition = MXServer.getMXServer().getConditionCache().get(conditionNum)      if(condition == None):          return True      conditionMbo = mbo if cond.getBoolean("VALISMAIN") else spec      return condition.evaluate(conditionMbo, False)     # function to throw invalid error message  def throwError(cond):      global errorkey, errorgroup      if(cond.getMboSet("MAXMESSAGES").isEmpty()):          errorkey = 'invalidMsg'          errorgroup = 'errorMsg'      else:          errorkey = cond.getString("MAXMESSAGES.MSGKEY")          errorgroup = cond.getString("MAXMESSAGES.MSGGROUP")    # main code: fetching the relevant specification record and processing the "specValid" function   # throws an error message if the attribute value is invalid  if(not mbo.getMboSet("SPECCONDITION").isEmpty()):      cond = mbo.getMboSet("SPECCONDITION").getMbo(0)      if(not specValid(cond, mbo)):          throwError(cond)  

This script will work when the user tabs out of a specification field such as ALNVALUE attribute of objects ITEMSPEC, ASSETSPEC, LOCATIONSPEC, WORKORDERSPEC etc. Hence, we also create an Attribute level launchpoint with VALIDATE event.

 

In Figure 3 below, we can see the custom Specification Restrictions sub-tab on Data Restrictions tab of Security Groups application.

image

 

Figure 3 - Specification Restrictions tab in Security Groups application

 

This solution extends regular data restrictions functionality to specification attributes for mandatoriness and validation. Similar approach can be applied for making specification attributes conditionally readonly.

[{"Business Unit":{"code":"BU059","label":"IBM Software w\/o TPS"},"Product":{"code":"SSLKT6","label":"IBM Maximo Asset Management"},"Component":"","Platform":[{"code":"PF025","label":"Platform Independent"}],"Version":"","Edition":"","Line of Business":{"code":"LOB59","label":"Sustainability Software"}}]

UID

ibm11129509