Showing posts with label Maximo Automation Scripts. Show all posts
Showing posts with label Maximo Automation Scripts. Show all posts

Tuesday, 4 September 2018

Random Examples of Maximo Automation Scripts

So far I have blogged about various capabilities offered by automation scripts in Maximo 7.6, needless to say I love working with Automation Scripts. I would like to share some random examples and usage of automation scripts in this blog. Please put your comments or suggestions.

Calling a Stored Procedure from Automation Script

I have a stored procedure in the database created as below:







This procedure needs to be executed on a scheduled basis. I can create a cron task or an escalation which will trigger the action. And, this action I can create as an automation script with Action launch point. The code snippet in this automation script will be as per the image below:

Throwing an exception in a Message Box using Automation Script

I have created a custom message in Database Configurations application which needs to be displayed to the user when the user selects a date less than the current system date while selecting required date in Create Requisition application. 

I will create an automation script of Attribute Launch point with validate event on MR.REQUIREDDATE attribute as per the images below:


















Taking action from Yes/No button in Message Box using Automation Script

Let us consider we have to throw a warning message with Yes or No button, and the user can select either of them. Based on the choice of the user, we need to take separate actions. The warning message is created in the Database Configurations application as below:

My requirement is when I select the Issue Unit for an item record in Item Master application, if an order unit is available and issue unit and order unit are not same, user needs to be thrown warning message with a choice if he/she wants to proceed. In case the user selects yes, nothing needs to be done but if the user selects no, the value needs to be cleared from the Issue Unit field. I will create an automation script with Attribute Launch point on ITEM.ISSUEUNIT attribute and event Run Action, as per the images below:



















Automation Script for Conditions

We can write automation scripts for custom conditions also which can then be used in workflows, or for achieving conditional UI, or in condition monitoring, etc. This type of automation script will be evaluated to either True or False and the boolean result is returned by the script using an implicit variable "evalresult".

Let us assume while approving a PO record, if the logged in user's PO approval limit is less than the total cost of the PO record, he should not be allowed to approve it. The automation script is created and source code is written as per below images.




























Once the script is written, we need to define the condition expression to refer this script. In Conditional Expression Manager application, create a new condition of type=CLASS and in the expression field, enter the notation as <script-name>:<launchpoint-name> and in the class field, enter the class reference as "com.ibm.tivoli.maximo.script.ScriptCustomCondition". See the image below.


Now, this condition 'CHECKLIMIT' is ready for use in the PO approval workflow process or in conditional UI to restrict the user in approving the PO record.

References:

Tuesday, 7 August 2018

Leveraging Automation Scripts for Inbound and Outbound processing of messages

We have used java classes to extend the main java processing classes to customize the processing of integration messages for inbound and outbound transactions. Maximo 7.6 has given us the capability to leverage automation scripts to apply our custom logic to integration components. We can create automation script and associate it to an object structure, enterprise service, publish channel, etc. To create an automation script for integration, in the Automation Scripts application you need to select the action Create > Automation Script for Integration
Next, you will have to select the integration component on which the automation script will run to apply your custom logic. Before that we need to understand what are the different customization points for inbound and outbound transactions that are available with automation scripts.
  1. The automation script for outbound object structure allows an outbound definition to be created which will have the custom processing code to be used during serialization of business object data into an XML message. The automation script for inbound object structure allows an inbound processing of the incoming XML message before it is mapped to the business data. The automation script name will be created by the system as OSIN.<object-structure name> for inbound processing or as OSOUT.<object-structure name> for outbound processing. 
  2. Object structure processing creates the integration message which is then forwarded to the publish channel for outbound transaction. Custom logic can be written in an Automation scripts to process the publish channel further, through either External Exit, user exit before external exit, user exit after external exit or event filter processing. If the publish channel selected for the automation script is MXINVENTORYINTERFACE, then the script name will be as below:
    • PUBLISH.MXINVENTORYINTERFACE.EXTEXIT.OUT (External Exit)
    • PUBLISH.MXINVENTORYINTERFACE.USEREXIT.OUT.BEFORE (User Exit before external exit)
    • PUBLISH.MXINVENTORYINTERFACE.USEREXIT.OUT.AFTER (User Exit after external exit)

    • After successful creation of the automation script for the publish channel, in the Publish Channels application, you can see that predefined class references get auto-populated in Processing class, User Exit Class or Event Filter Class fields. The processing class field will have the value as "com.ibm.tivoli.maximo.script.ScriptExternalExit" if we have used external exit with the automation script or the user exit class field will have the value as "com.ibm.tivoli.maximo.script.ScriptUserExit" if we have used user exit class.

    • For outbound integration messages, Maximo will start an integration transaction. The object structure provides an internal record data (irData) element to a publish channel, which is then processed through a custom logic and an external record data (erData) element is constructed before the message is forwarded to its destination. We have created an automation script (PUBLISH.ES_MXITEMINTERFACE.EXTEXIT.OUT) with a publish channel that is created on MXITEM object structure. In this object structure, we have included a non-persistent attribute "ITEMNAME_NP" which should be combination of item number and description which needs to be sent in outbound transaction. The source code is as below:
  3. Enterprise service provides asynchronous and synchronous processing of inbound integration messages. During asynchronous processing, external service opens a connection and sends a  enterprise service request and does not require a response. However, during synchronous processing, external service opens the connection to send the enterprise service request and will maintain the connection till a response is received. Automation scripts provide the following customization points during the processing of inbound or outbound integration messages to be used with enterprise services:
    • Inbound Request - User Exit before external exit - SYNC.<enterprise-service name>.USEREXIT.IN.BEFORE
    • Inbound Request - User exit after external exit - SYNC.<enterprise-service name>.USEREXIT.IN.AFTER
    • Inbound Request - External Exit - SYNC.<enterprise-service name>.EXTEXIT.IN
    • Outbound Response - User Exit before external exit - SYNC.<enterprise-service name>.USEREXIT.OUT.BEFORE
    • Outbound Response - User exit after external exit - SYNC.<enterprise-service name>.USEREXIT.OUT.AFTER
    • Outbound Response - External Exit - SYNC.<enterprise-service name>.EXTEXIT.OUT

    • After successful creation of the automation script for enterprise service, you can see in the Enterprise Service application, for this enterprise service record predefined class references get populated as "com.ibm.tivoli.maximo.script.ScriptExternalExit" in the processing class field or as "com.ibm.tivoli.maximo.script.ScriptUserExit" in the user exit class field.

    • For inbound integration messages, external system starts the integration transaction and the incoming integration message provides an erData element to an enterprise service. Through a custom code, this element is processed to construct an irData element before the message is forwarded to the associated object structure. Let us assume that we are using out of the box enterprise service, 'MXPOInterface' to processes inbound messages to MXPO object structure. If for any incoming PO record, buyer (PURCHASEAGENT) is null, we need to use the value of the person who has initiated the PO record in this field before saving it to PO object. Automation script is created as per the image below:
After the above automation script is created, you can see in the related enterprise service record, processing class field is auto-populated with the predefined class reference. See the below image:

In case you want to delete the above script, please remember to remove the class reference explicitly from this field in the Enterprise Services Application.

References:

  1. Creating automation scripts for integration
  2. Automation Scripts with Object Structures
  3. Automation Scripts with Publish Channel
  4. Automation Scripts with Enterprise Service
  5. Examples from IBM Knowledge Center

Thursday, 26 July 2018

Dynamic values in Lookups - Automation Script Example

Let us consider field level validation of maximo business object attributes. When we are talking about field level validation class, we will directly jump to extend the MboValueAdapter class. Say, in service request records we have to set the value of Phone field of reported by, if it is null, with the value of Primary SMS field in the person record. We will write a java class in a custom package, custom.module.mbo.NewCustomFldClass, in its action method we will write the business logic. After building the ear file and deploying it, we will enter the class path in SR.REPORTEDBY attribute in Database Configurations application and then apply the configuration changes. Let us check the Java source code in the image below:


Whenever a value will be entered in the SR.REPORTEDBY field and tabbed out, above class file will do its action. This can be achieved alternatively by creating an automation script with Attribute launch point with Run Action event on SR.REPORTEDBY attribute, with a simple source code as below:


Now, what if we need some validation and action along with a lookup. We will write the new java custom class which will extend psdi.mbo.MaxTableDomain. We can use the init(), validate() and action() methods as per our requirement and in addition to these, we have one more action getList() for our use. In this custom class we can set the relationship to the object from which we want the values to be fetched and a list criteria with the where clause to filter data from that object. Inside the getList() method we can set additional where clause to filter data.

Conditional lookups for a field can also be achieved in Application Designer, for which I have explained with an example in another blog

Let us consider that when user clicks New Row in Materials sub-tab of Plans tab in Work Order Tracking application, Item field lookup should display all items available if it is a direct issue, else the lookup should display a set of values of items for which Inventory records exist in the default storeroom of the reported by user. To achieve this, following table domain is created as the first step:

Next, we want to create an automation script with Attribute Launch point and event Retrieve List. This is done as per below images:


The source code of this automation script is written as per below image:


References:

Monday, 23 July 2018

Fetching Mbo Sets using RelationShip or MXServer - Automation script Examples


I would like to draw your attention towards one important thing which we keep using in our java codes or Jython scripts but we do not use them properly. This is about getMboSet() method. As we are aware there are two ways of fetching an Mbo set, either by using a relationship or MXServer. Improper use of this may lead to performance issues, poor memory management, etc.

For example, I am working with save event of Object launch point on WORKODER object. I need to set a value in all related tasks which are in approved status. There is a relationship between WORKORDER object and WOACTIVITY object which can give me the set of all related tasks of the workorder which are in approved status. It will be easier for me to use this relationship to fetch the WOACTIVITY records. Please keep in mind that when I am accessing the Mbo records using a relationship, it is included in the same transaction as the parent Mbo Set and if I call a save on the parent Mbo Set, all MboSets in the current transaction will be saved. So, the Mbo sets fetched using relationship do not require save to be called explicitly because their save is called when their parent is saved.

In the above code snippet, I can also use a setWhere clause to specifically select some of the records, this new where clause will be appended with the existing where clause of the relationship and you will have to call a reset() method to execute and update the resultset obtained using the relationship. 


Above script will save the WORKORDER records and the related WOACTIVITY records in the current transaction.

Let us consider that the relationship from WORKORDER object to WOACTIVITY does not exist which may satisfy the search criteria we are looking for. In this case, we can create one on the fly as below: 




The parameter “$WOTASK” is a temporary relationship name, “WOACTIVITY” is the child object, and “parent=:wonum and siteid=:siteid and istask=1 and status=’APPR’ and targcompdate is not null” is the relationship where clause. The values of :wonum and :siteid will be taken from the current Mbo record from WORKORDER object.

While fetching MboSets from MXServer, we have to be careful about a few things.
  1. Always use setWhere() and reset() methods with this MboSet. The setWhere() method will reduce the number of Mbo records being fetched and reset() method will ensure fetching of data with the where clause.
  2. If the Mbo set is being fetched only for traversing and not for any addition or update, it is a best practice to set the DISCARDABLE flag as true. If the Mbo set is being fetched for read-only purpose it is a good practice to set the NOSAVE flag as true. These steps will shorten the looping time of the transaction.
  3. If any setValue method is used for some Mbo record of the MboSet, or any add or update is being done on the Mbo set, it is required to call the save() method on the set. Mbo sets fetched from MXServer needs to be saved before the set is closed otherwise the changes will be lost.
  4. Always use clear() and close() methods on the Mbo set fetched using MXServer if it is not required anymore. This will release memory and the database resources

Let us consider one example. Every Saturday, we need to run an escalation to update the actual cost of all issue transactions of active inventory records of all capitalized items that have happened between last Monday and Friday. We have written an escalation to run every Saturday on Inventory Object which calls an Action. Keeping in mind all the above rules, this action logic is written in an automation script with Action launch point, below is the sample source code of the script.


Thursday, 5 July 2018

Using Maximo Constants in SetValue methods of Automation Scripts - What, Where, Why

I am blogging again on automation scripts to share my learning with the readers and the contents used in this blog are examples only. Hope this blog helps someone and suggestions/comments are always welcome.  In Maximo 7.6, we have different launch points Object, Attribute and Action to associate to the automation scripts. We already know when to use what. Still, I want to emphasize a few points. 

We have seen in object level java classes order of execution of the methods. The initValue() method is the first method executed after the constructor and is used to initialize attributes on new records and to set default values. The init() method is called after initValue() to set the related attributes as read-only and save() method is executed at the end. Now, coming back to automation scripts, for object launch points we have the events as shown in the image below:

I have a requirement to set the Internal checkbox on PO record creation if the logged in user belongs to some security group X and user should not be able to edit this value. I can use an object launch point on object PO with Object Event Condition as ":&USERNAME& in (select userid from groupuser where groupname='X')" and event as "Initialize Value" and in the script source code I can simply use the below lines:



Let us consider another example where we want to populate the values of PO.SHIPTO and PO.SHIPTOATTN fields based on the PO.PURCHASEAGENT and its related person record. We can use an automation script with object launch point on object PO and event would be Save. On add or update of a PO record, this will be triggered on save. Please see the below images for this example.

Please note that PURCHASEAGENTPSNREC is a relationship name from parent PO object to child PERSON object with where clause "personid=:purchaseagent". Also, please note that while setting the values I have not used any Mbo Constant. I will come back to this later.

Let us now consider field level java classes. The field validation or action gets invoked when tabbing out of a field, or when a field is being set from the back end. The typical order of execution of methods in a field level class is: initValue(), init(), validate(), action(), hasList(), getList(). The initValue() method is used for initializing fields for new records, init() method sets read-only fields, validate() method can be used to verify whether the new value for the attribute is valid, and action() method can be customized to contain the business logic or update any other object or attribute. Similar thing we can keep in mind while using Attribute launch points. Please see the below image.
We can use Initialize Value event for an Attribute launch point to achieve same result as we use initValue() method in a field level java class, Initialize Access Restriction event can be used on the same lines as init() method, Validate event for validate() method, Run action event for action() method, and Retrieve List event will work in similar lines as getList() method. It is easy to correlate the order of execution of these events with the methods of field level java customization. For example, when we add a new line record in PO Lines tab and select an item, we need to make fields X and Y read only, copy a few fields from related item master record if it is a KIT item. We have to do all this when we enter or select the value in ITEMNUM field. We wrote the logic to make fields X and Y read-only in an automation script with Attribute launch point on POLINE.ITEMNUM attribute using event Run Action and used another automation script to copy the values from related item master record to POLINE fields with Attribute launch point on POLINE.ITEMNUM attribute using Validate event. 

Let me come back to MboConstants and draw your attention towards an error we got while we were changing the capitalized status of an item in Item Master application. We got an error as Inventory Object is read-only. When one item is changed to capitalized status, it tries to update all  the related inventory records and save on INVENTORY object is called which in turn calls save method on child objects, INVBALANCES, INVCOST, etc. We have configured some access restriction in the fields of INVENTORY object and one automation script was running to set a few fields of INVENTORY record. This automation script was written on Object launch point with Save event. To resolve this we had to use MboConstants.NOACCESSCHECK in the setValue method used in that script. This was to make sure that those fields can be overwritten without having to rely on whether the fields are accessible on Mbo level.

Similar to the above example, we had not used any Mbo Constant in the setValue methods in our object launch point script with Save event. In this case, however, it is not going to loop through an array of records. It is setting the values of the same PO record on which purchaseagent field is not null. So, even if we do not use MboConstants.NOACCESSCHECK with the setValue method it is not going to throw any error in Purchase Order application. It is recommended to use with the setValue method so as to ensure that the PO record is accessible at any point of time when save on PO object is called from another application or external event. So, the modified script should be as below:
And, it is also a good practice to close a MboSet if it is not to be used anymore.

You can refer the following sites if you want to know about what Maximo constants we need to use while setting values of attributes.
Using field flags to set attribute content
Flags for setValue methods
Mbo Constants in Maximo

Thursday, 2 June 2016

Configuring Work Logs for an application

We all are aware that our world is a client driven world. We need to get workarounds for most of the requirements we gather. We came across one such requirement to configure work logs for Invoice application. So, we added relationships from INVOICE object to WORKLOG object as recordkey=:invoicenum and class='INVOICE'. We included the work log tab in our application xml. When we decompiled the WORKLOG.class in psdi.app.ticket folder in MAXIMO 7.6, we could see it includes PO and PR with earlier TICKET and WORKORDER class records only. So. while you want to add a record to WORKLOG from Invoice application, system is unable to decipher what is the parent class, what to set in recordkey field, etc.

We explored two approaches to get a work around to the above stated problem. One is to insert records into APPFIELDDEFAULTS table using below sql insert scripts



Next approach which is also very effective is using automation script. We created an object launch point on WORKLOG object with event as Initialization and wrote our source code as attached in the images below:





























So far so good. We had another requirement to add similar tab to Item Master application. We had configured every thing accordingly but on click of New Row button in the Work Logs tab, we encountered the below error:












We found out that ITEM object is at ITEMSET level and does not have ORGID attribute, so we were thrown this particular error from the system. So, we rolled back all configuration changes we had done to add the Work Logs tab in Item Master application as any other application with Logs tab. We could not find a solution. As a work around, we created a custom object which had all the attributes as WORKLOG object, created a new relationship from ITEM object to this custom object and managed to add records to this new custom object. If someone had encountered such a scenario and were able to find a better solution, please feel free to share your thoughts.

References: