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

Friday, 1 June 2018

Maintenance of Metered Assets - PM Counters and Job Plan Sequences


We are aware that we use job plan sequences in Preventive Maintenance to rotate through a series of job plans. For example, you can assign monthly, quarterly, and yearly job plans for work on an asset. The job plan is selected by dividing the PM Counter by the Sequence number. The largest, divisible sequence number will be selected.

This is easier to understand if we take an example of a time-based PM record. Say, you have separate set of tasks for monthly maintenance and semi-annual maintenance. You create two job plans MnthJP and 6MnthJP and you associate these two job plans to a PM record with first job plan with sequence# 1 and second job plan with sequence# 6. Initial PM counter value is 0, so if the frequency is 1 Month, the first work order will be generated with job plan MnthJP and PM counter will be incremented to 1. For the second month also, work order will be generated with job plan MnthJP. Dividing the PM counter value 2 by job plan sequence# 1 results in a whole number, so job plan MnthJP was selected.  Now, if I set the PM counter to 5, and then select the Generate Work Order action for this PM, system will create a work order with job plan 6MnthJP and PM counter will be incremented to 6 (PM counter 6 / Job Plan sequence# 6 = 1, a whole number).

Let us take an example of a metered asset, say, we need to run maintenance schedule for a car servicing every 500 KMs, wheel rotation and oil change with normal car servicing every 1000 KMs, wheels and oil change, wheel alignment with normal car servicing every 4500 KMs and every 10000KMs, the car needs Body painting, Wheel Change and Alignment along with normal car servicing. Let’s also, assume this car runs 50 KMs per day. There are job plan JP500, JP1000, JP4500 and JP10000 with separate tasks. Now the question is how to set the sequences of these job plans so that when work order is generated, it selects the correct job plan.

We have associated a continuous meter with our asset:


Let us create the Preventive Maintenance record associating this Asset. In the Frequency tab, we will use a meter based frequency as below:

Now let us see how we can achieve the job plan sequencing to apply the job plans to work orders.
If we use the action to View the Sequence, we will understand more.
We can go to the asset and record the meter readings. For the first time, we will put meter reading as 500. And, let’s generate the Work Order from the PM record.
Work order 1041 gets created for the first 500 KMs with job plan JP500 as below. 500 meter reading divided by frequency 500, will result in 1. This value is divisible by only job plan sequence# 1.
At this point, we can see that the PM Counter of the PM record has increased to 1 and if we generate the Work Order from the PM record (with Use Frequency Criteria unchecked), this time the work order will be generated with job plan JP1000 because after last meter reading of 500KMs with frequency 500, next meter reading should be 1000KMs, and 1000 divided by 500 (frequency) will result in 2 which is divisible by largest job plan sequence# 2.
Now, if we set the PM Counter to 8, you can see that in the PM record, the job plan value has changed to JP4500.  Let us generate the work order again with “Use Frequency Criteria?” unchecked. This time work order will be generated with job plan JP4500 for 4500KMs. The meter reading 4500, if divided by frequency, 500, will result in number 9 which is easily divisible by largest job plan sequence#9.













After generation of the work order from above step, PM Counter will be 10 and job plan in the PM record will be changed to JP1000, as 5000KMs meter reading if divided by frequency, 500, will result in 10, which is easily divisible by largest job plan sequence# 2.

Similarly, if we set the PM counter value to 19 and generate the work order, with unchecking the “Use Frequency Criteria?” the system will generate a work order with job plan JP10000. 10000KMs meter reading divided by frequency 500, will result in 20 and this value is easily divisible by largest job plan sequence# 20.

References:


Monday, 28 May 2018

Capitalized Items in Maximo


We had a requirement at hand to change the capitalized status to non-capitalized of a list of items in bulk. When we checked how many items were available at that time based on the given criteria, we found more than a thousand. It was not possible to go to Item Master application, browse through each and every item and use the action Change Capitalized Status. We were left with no option but to take the route of bulk update of these items through SQL. We need to consider some key points in doing so.

What is a capitalized item? An item is capitalized when it is recorded as an asset, rather than an expense. What does it mean in Maximo? When the capitalized check box is checked for an item, then the cost of the part/asset object is not charged to the work order (or other associated charge account) when it is issued from the Storeroom. Rather, it is placed on the Organization books as a depreciable asset and would be expensed on a monthly basis over time. If the capitalized check box is unchecked for an item, then the cost of the rotating part/asset object will be charged to the appropriate work order and associated GL accounts upon use.

In Maximo, you go to Item Master application. Select the item which you want to capitalize. You select the action, Change Capitalized status. In the Change Capitalized Status window, you need to enter an account code that you plan to use as the capital GL account. A default capital GL account is not provided. When the item is changed to capitalized status, the storeroom status is changed to capitalized, and the average, last, and standard costs are set to zero in all storerooms containing the item.

For each inventory record, i.e. for all storerooms containing the item, the account code manually entered in the Capital GL Account field, gets associated. In the CONTROLACC column of the INVCOST table, the inventory control account code is replaced with the capital GL account code for all storerooms containing the item. In effect, this transfers the charge or value associated with the item from the inventory control account to the capital GL account. In the INVCOST table, for each row that corresponds to an item, the SHRINKAGEACC and INVCOSTADJACC columns are cleared. 
For each storeroom that contains the item, a CAPCSTADJ transaction is written to the INVTRANS table, INVTRANS.GLDEBITACCT defaults to manually entered capital GL account value and INVTRANS.GLCREDITACCT defaults to INVCOST.CONTROLACC or LOCATIONS.CONTROLACC, or INVLIFOFIFOCOST.CONTROLACC.



The correct path to be followed is to first add the item to the storeroom and only after that change the capitalized status. This is because Maximo 'Change Capitalized Status' functionality is based on existence of inventory records. Additionally, Inventory transactions of type CAPCSTADJ are created to record the transactions that involve costs with changing the capitalized status back and forth. 

Coming back to our requirement at hand. The following SQL queries we had to run as per the sequence below.
1.       Let us assume the criteria to identify the items is all lot items associated with commodity group=’XYZ’.
UPDATE MAXIMO.ITEM SET CAPITALIZED=1 WHERE COMMODITYGROUP='XYZ' AND LOTTYPE=LOT AND CAPITALIZED=0;
2.       Let us assume capital account to be provided for these items is ‘6170-450-200’.
UPDATE MAXIMO.INVCOST SET AVGCOST=0.0, LASTCOST=0.0, INVCOSTADJACC = '', SHRINKAGEACC = '', CONTROLACC = '6170-450-200'
WHERE ITEMNUM IN (SELECT DISTINCT ITEMNUM FROM MAXIMO.ITEM WHERE COMMODITYGROUP='XYZ' AND LOTTYPE=LOT AND CAPITALIZED=1);
3.       Also, every time an item is made capitalized, a record needs to be added to INVTRANS table for every inventory record with transtype = CAPCSTADJ. This will be the trickiest query.
INSERT INTO MAXIMO.INVTRANS (INVTRANSID, ITEMNUM, ITEMSETID, STORELOC, TRANSTYPE, TRANSDATE, ORGID, SITEID, ENTERBY, CONSIGNMENT,
QUANTITY, CURBAL, PHYSCNT, OLDCOST, NEWCOST, LINECOST, MEMO, GLDEBITACCT, GLCREDITACCT) SELECT INVTRANSSEQ.NEXTVAL INVTRANSID, ITEMNUM ITEMNUM, ITEMSETID ITEMSETID, LOCATION STORECLOC, 'CAPCSTADJ' TRANSTYPE, SYSDATE TRANSDATE, ORG ORGID, SITE SITEID, 'MAXADMIN' ENTERBY, 0 CONSIGNMENT, QTY QUANTITY, BAL CURBAL, CNT PHYSCNT, CST OLDCOST, 0.0 NEWCOST, (QTY*CST) LINECOST,
'Changing item to Capitalized' MEMO, '6170-450-200' GLDEBITACCT, CONTROLACC GLCREDITACCT FROM (SELECT ITM.ITEMNUM ITEMNUM, ITM.ITEMSETID ITEMSETID,
LOC.LOCATION, LOC.ORGID ORG, LOC.SITEID SITE, INV.CURBAL QTY, INV.CURBAL BAL, INV.PHYSCNT CNT, CST.AVGCOST CST, LOC.CONTROLACC CONTROLACC
FROM MAXIMO.ITEM ITM, MAXIMO.LOCATIONS LOC, MAXIMO.INVBALANCES INV, MAXIMO.INVCOST CST
WHERE ITM.COMMODITYGROUP='XYZ' AND ITM.LOTTYPE=LOT AND ITM.CAPITALIZED=1 AND ITM.ITEMNUM=CST.ITEMNUM AND ITM.ITEMSETID=CST.ITEMSETID
AND CST.ITEMNUM=INV.ITEMNUM AND CST.ITEMSETID=INV.ITEMSETID AND CST.LOCATION=INV.LOCATION AND CST.SITEID=INV.SITEID
AND INV.LOCATION=LOC.LOCATION AND INV.SITEID=LOC.SITEID); 

References:

Friday, 12 January 2018

The Powerful Dot Notation in Maximo

We are familiar with displaying additional information through a relationship in Maximo. If I want to display the asset's serial number in Work Order Tracking application when the asset field is populated, I just have to add a textbox in Application Designer for WOTRACK application and reference the RELATIONSHIPNAME.ATTRIBUTENAME in the attribute property of the textbox. For the WORKORDER object, there exists an OOB relationship "ASSET" to the child table ASSET. So, I need to simply use ASSET.SERIALNUM in the attribute property of the textbox. When an asset is entered in the Work Order record, this textbox will display the serial number.

Maximo relationships are same in terms of SQL joins and we are aware that even multilevel relationships can be used in Maximo. For example, if I have to send a notification to the affected person from a Service Request once it is created. In the communication template for this notification, I can specify a dataset role on SR object, in which value will be :AFFECTEDPERSON.EMAIL.emailaddress. AFFECTEDPERSON is the first level relationship name from parent object TICKET to child object PERSON and EMAIL is the next level relationship name from parent object PERSON to child object EMAIL. This will help is identifying the email address to which notification should be sent.

If we need to set a value in a field from a related field on some condition, we can use the dot notation here too. We have to set the value of the purchase agent of a PO with the value of the ship to person. We can create an action on PO object of type SETVALUE for parameter/attribute PUCHASEAGENT and in the value we can associate the ship to person ID using :SHIPTOPERSON.personid, where SHIPTOPERSON is the relationship name from parent object PO to child object PERSON. We can now reference this action from either a workflow or escalation whichever is applicable.

We can use the dot notation in building conditions also in Conditional expression manager. For example, a condition can be created to check if the bin number in related inventory balances record is same as default bin of inventory record, :invbalances.binnum = :binnum, where INVBALANCES is the relationship from parent object INVENTORY to child object INVBALANCES.

In recent times, we have also seen the extension of this powerful dot notation to result set portlets. I have tried this in Maximo 7.6. I create an object structure "REP_INVENTORY" for reporting purpose as below:

Then I go to Report Administration application and from more actions, I select "Set Report Object Structure Security". Provide access to MAXADMIN security group for the above object structure as below:

Now, in the Inventory application say you have created a query to display the inventory records of the logged in user, "Inventory of User Storeroom". If you want to set up a new result set in some existing start center, when you select to edit the portlet, you will be able to select the application, query and the object structure created above. And, you will be able to see the child table(s) along with the parent table under Object List.

When you select the attributes from the child table, you can see the <RELATIONSHIPNAME>.<ATTRIBUTENAME> in selected fields.

Once you save and finish the result set portlet setup, you will be able to see the resultset as below:
Recently I read an interesting blog about one more power packed feature of using this dot notation. I could not resist myself in trying that out and yes, it is worth mentioning about. You can use this relationshipname.attributename in cross-over domain's source and destination fields. Let us consider an example. We create a custom attribute in TICKET object, which is say, CUSTAPPROVER. In this field in Service Requests application, we need to display the supervisor of the supervisor of the affected person. So, we create a cross over domain as per the image below, which we associate with the CUSTAPPROVER attribute in TICKET object.

Hope you like reading this blog and your suggestions/comments are most welcome.

References:

Monday, 8 January 2018

Exploring Automation Scripts with some examples


Lets explore some of the solutions achieved through automation scripts in Maximo. If one has worked with customization or extension of Java classes in Maximo framework, he/she would be well versed with these solutions. But here we will follow the idea of achieving these solutions without having to think of building the ear file and deploying it into the web server. These solutions may not be agreed upon by every one but this is to show how automation scripts can be helpful in achieving the requirements. And, to highlight that automation scripts can be used in place of Java class files to achieve the functionality in similar fashion at the cost of no extension to the existing ear file. The contents used in this blog are examples only. Hope you like reading my blog. Suggestions / comments are most welcome.

Trade-off for using Automation Script over Cross Over Domains with YORN fields

You add a cross-over domain to an attribute when you want to copy one or more attribute(s) values from a related object to current object's attributes. For example, when you select an item in a Purchase Order Line record, you want to cross over the item's description, issue unit, order unit, etc. from Item Master record, cross-over domain can be an effective tool. Now, think about this. I have this requirement to copy the Storeroom and Storeroom Site from the logged in user's profile to a PO record when user checks the internal checkbox. Ideally, we would suggest cross-over domain to be used with PO.INTERNAL attribute. But, is it really this simple? We go ahead to create a cross-over domain to copy MAXUSER.DEFSITE and MAXUSER.DEFSTOREROOM (source fields) to PO.SITEID and PO.STORELOC (destination fields). 

When we try to associate this cross over domain to PO.INTERNAL attribute in Database Configurations application, we get the error: BMXAA0664E - Domain must be null for data type YORN. This is but obvious. So, other option left for us to explore is using the automation script. We can easily create an automation script on object "PO" with an attribute launch point on attribute 'INTERNAL" to achieve our desired solution.


Triggering the automation script - Which Launch Point to use?

I would  like to explain this using an example. We had a requirement that for a Kit item, we had to store the values of its sub-assemblies in one attribute. We identified the long description to capture this for a kit item. Next question is when to trigger this solution, when we identify an item to be a Kit item or when we save it. In Item Master application, we identify Kit items with checking of the "Kit?" checkbox and the "Item Assembly Structure" tab lists out the children this item has. We can make use of these out-of-the box attribute (ISKIT) and object ITEMSTRUCT to arrive at the solution. But generally, as and when we check the checkbox, "Kit?", for an item, Item Assembly structure is not available at that point of time. So, if we create an automation script with attribute launch point on attribute ITEM.ISKIT, it will not serve our purpose. Moreover, we need to associate the item assembly structure of this kit item to its children items/assets. Let us assume there is a custom relationship "CHILDITEMS", on parent=ITEM, child=ITEMSTRUCT with where clause as "itemsetid=:itemsetid and parent=:itemnum". we can use this relationship to fetch the children for the kit item.
We created an automation script with object launch point on object=ITEM as below:





















And, we can use the following code snippet:


Automation Script for Calculations

Another example which I want to share involves some calculations. We need to store the Turns Ratio for items in Storerooms, which is calculated as net issues divided by average inventory for last 52 weeks. Net Issues is the number of issues minus returns from the inventory in last 52 weeks. And, avergare inventory was calculated by multiplying the sum of inventory balance of each week in last 52 weeks with average cost of inventory and then dividing this number by 52.

For this solution, we created a custom table WEEKLYBAL and had implemented a batch job (you can use escalation or a cron task), running every Saturday at a fixed time to copy the current balance of each inventory item (storeroom, item number, item set, system date as WEEKDATE, current balance as CURWEEKBAL and average cost), assuming ISO week starting on Sunday and ending on a Saturday. We also created a custom attribute, "INVENTORY.TURNRATIO", to store the value of all our calculations for each Inventory record. And, one more escalation can be created every Sunday after midnight in early hours of the day to trigger one action which in turn can be an automation script to do the following.
If you want to calculate the net issues for each inventory item using a SQL query, it would be as below:
select A.itemnum, A.storeloc, A.siteid, abs(sum(A.numofissues) - sum(A.numofreturns)) NetIssues  from
(select itemnum, itemsetid, storeloc, siteid,
case when issuetype='ISSUE' then actualcost 0 end as numofissues,
case when issuetype='RETURN' then actualcost else 0 end as numofreturns from matusetrans
where transdate between (trunc(sysdate,'IW')- 51*7) and (trunc(SYSDATE, 'IW')+6))A
group by A.itemnum, A.itemsetid, A.storeloc, A.siteid;
For this above part, we can create a relationship from INVENTORY object to MATUSETRANS object. Let us assume there is a relationship "ISSUERETURN52WK", parent=INVENTORY, child=MATUSETRANS with where clause as "itemnum=:itemnum and itemsetid=:itemsetid and siteid=:siteid and storeloc=:location and (transdate between (trunc(sysdate,'IW')- 51*7) and (trunc(SYSDATE, 'IW')+6))". Code snippet to calculate the net issues for all inventory items would likely be as below:

And, the average inventory for each inventory item will be calculated using the below query from the custom table "WEEKLYBAL":
select itemnum, itemsetid, storeroom, siteid, sum(curweekbal*avgcost)/52 from WEEKLYBAL 
where (weekdate between (trunc(sysdate,'IW')- 51*7) and (trunc(SYSDATE, 'IW')+6))
and trunc(weekdate, 'IW')+5 = trunc(weekdate)
group by itemnum, itemsetid, storeroom, siteid;

To simplify this, we have a new relationship "INVWKBAL52" for parent=INVENTORY and CHILD=WEEKLYBAL with where clause as "storeroom=:location and siteid=:siteid and itemnum=:itemnum and itemsetid=:itemsetid and (weekdate between (trunc(sysdate,'IW')- 51*7) and (trunc(SYSDATE, 'IW')+6)) and trunc(weekdate, 'IW') + 5=trunc(weekdate)". Turn ratio can be calcuated and stored in the custom attribute using an extension to the above snippet as below:

Wednesday, 22 November 2017

Using Interface Tables to Insert Records - Continuous Vs Sequential Queue

In our world, we come across different requirements and problem statements for which we need to provide solution. It is not necessary that each solution we design or implement would be agreed upon universally. We had this requirement to generate records automatically for one of the application in MAXIMO. At the same time, record needs to be created in the parent object as well as records need to be entered to its child object. So, definitely we need to think about Object Structure, Enterprise services, External Systems and off course Interface Tables to insert records to our MAXIMO main objects.

For this data import to MAXIMO through interface tables, we need to consider or keep in mind the following:
  1. Object Structure - The object structure with the required objects and parent-child relationships should be created. Exclude/Include fields should be used appropriately keeping in mind what columns need to be included in the interface table.
  2. Enterprise Service - Enterprise Service should be created with the above created object structure and the intended name of the Interface Table mentioned. You can check to enable message tracking.
  3. External System - You can create a new external system or use an existing one but ensure that end point selected is "MXIFACETABLE", sequential queues and continuous queues properly configured. 
    • Outbound Sequential Queue = jms/maximo/int/queues/sqout
    • Inbound Sequential Queue = jms/maximo/int/queues/sqin
    • Inbound Continuous Queue = jms/maximo/int/queues/cqin
    • In the Enterprise Services tab for this external system, add the above created enterprise service, check the "Enabled?" check box, check or uncheck the "Use Continuous Queue?" checkbox.
    • From action menu, now select the Create Interface Tables action. In the dialog, select the interface table name mentioned with the enterprise service and click on Create button. It will take a few seconds to process your request.
  4. Cron Task - In Cron Task Setup application, open "JMSQSEQCONSUMER" cron task and ensure that two cron task instances, one for sequential In and one for sequential Out are active. Next, open the "IFACETABLECONSUMER" cron task. A cron task instance should be present which is active with its scheduled defined. The properties for this cron task instance should have QUEUETABLE=MXIN_INTER_TRANS and END POINT=MXIFACETABLE.
  5. Writing data to Interface Table - At this stage, everything on the Maximo side has been set up, and we need to do to create some kind of routine to send data to the interface tables. The cron task IFACETABLECONSUMER, then polls the inbound interface queue table for new records to process as per the schedule set. 
    • Automation Script: For every record that needs to be added to the MAXIMO object, we need to add a new row to MXIN_INTER_TRANS object as well as to the Interface Table. For our convenience, we have used an automation script to include insert scripts for MXIN_INTER_TRANS and the interface table created above.
    • Cron Task: To trigger or initiate the above automation script, we have set up a new cron task with class=com.ibm.tivoli.maximo.script.ScriptCrontask. A new cron task instance also needs to be created with "Active?" checkbox checked and appropriate schedule defined. The properties for this cron task instance include SCRIPTARG=0 and SCRIPTNAME=<above automation script name>
With above configurations, we were able to create new records for the application, as stated above. Now, moving over to the confusion over using continuous queue or sequential queue for this approach. We had initially used continuous queue for this inbound processing of records to MAXIMO objects. If an incoming message gets stuck in sequential queue, other subsequent incoming messages are not processed until the error message is either reprocessed or deleted. To avoid this, we had gone ahead with continuous queue. It was working fine in our development instance. When this was moved to TEST and Production instance, we started facing an error of Database Error 1 in some of the incoming messages. 

I need to mention here that this database error 1 had come for primary key attribute of the main object in the object structure.We checked the logic in automation script to see if we were trying to insert any duplicate value to this attribute. This may have been due to continuous queue trying to insert two records with same ID generated. If we would reprocess the message with status=ERROR, it eventually gets processed. To handle this, we tried two approaches.
  1. We tried with increasing the Max Try Count, this issue got reduced but it was not fully resolved, i.e., the number of failed messages decreased but issue was still prevalent for a few messages. 
  2. We changed the inbound processing queue to sequential. We have never come across the database error 1 for our primary key attribute again and it has been seven to eight months since :) 
With the above experience, we can conclude that if the order of processing is important, like in our case above, we should use Sequential Queue rather than Continuous queue. Continuous inbound processing works better for transaction records like invoices, etc. Please do share your experiences and thoughts.



Saturday, 22 April 2017

Exploring Automation Scripts and their migration

We have already explored Automation Scripts in Maximo 7.5 and 7.6 to customize as per our requirements from Object, Attribute and Action launch point. Needless to say, automation scripts have made life easier not in one but in many ways for developers. There are many bloggers who have jotted down their thoughts around using action automation script. You can refer the blogs listed below to know about how automation script can be used to control an action from a push-button. And these work perfectly as an action called from any escalation as well.

  1. Action Automation Script
  2. Automation Script to control its action from a pushbutton
We do have Attribute Launch Points which have made it easier to address field level validations as well. Whenever a value is specified for that field, it may be a change of value as well, this attribute launch point which is associated with the field gets eecuted. Automation Scripts have given us the flexibility to bind our desired IN/OUT/INOUT variables from the launch points to values to/from object attributes.

Let us consider below two examples for Attribute Launch points:
1. Say, we want to clear a value in one of the field 'ATTR2' based on the value of some other field 'ATTR1' of object, say, 'TAB1'

  • I will create an attribute launch point on Object=TAB1 and Attribute=ATTR1
  • I will keep the launch point even as run action.
  • Let us keep the script language=Jython and log level=ERROR.
  • I am not going to create any variable and in the automation script, I will write the following code snippet:

strAttr1 = mbo.getString("ATTR1")
if (strAttr1=='XYZ'):
mbo.getMboValue("ATTR2").setValueNull(2L)
                PS: I have used setValueNull(2L) because there is a data restriction on the field ATTR2.
2. Next say, we want to set the count frequency when we add/update the value of ABCType in an inventory record-

  • I will create the attribute launch point on Object=Inventory and Attribute=ABCTYPE.
  • I will keep the launch point event as run action.
  • I will keep the script language=Jython and log level=ERROR.
  • I will declare three INOUT variables var_AFreq, var_BFreq and var_CFreq, with binding type=MAXVAR (value source from a MAXVAR). As soon as I select MAXVAR in binding type. Global Binding Value becomes required. We can select ABC Breakpoint-cycle count frequency and associate A_CCF, B_CCF, C_CCF, respectively. We can also copy the value in Launch Point Biding Value field also.
  • I will declare one IN type variable, var_ABCType with binding type=ATTRIBUTE (value sourced from an attribute) and launch point attribute as ABCTYPE
  • I will declare one more variable of type=OUT, var_CCF with binding type=ATTRIBUTE and launch point attribute=CCF
  • In the automation script, following code snippet, I need to write:

        if var_ABCtype=='A':
            var_CCF=var_AFreq
        if var_ABCtype=='B':
var_CCF=var_BFreq
                    if var_ABCtype=='C':
                       var_CCF=var_CFreq


 Now, let us talk about migration of automation script with an Attribute launch point. Whenever we are migrating an automation script and attribute launch point which has an event other than validate say, Initialize Value or Run action, in the source environment, we found that on the target environment, the event for the attribute launch point is selected as Validate.
While creating the package in the source environment, we had selected OOB migration group SCRIPTCFG in the package definition. We had kept the script names in the where clause for migration object DMSCRIPT ad launch point names in the where clause for migration object DMLAUNCHPOINT as below

  •      autoscript in ('script1', 'script2'...)
  •      launchpointname in (launchpoint1', 'launchpoint2'...)


EVENTTYPE attribute in object SCRIPTLAUNCHPOINT is non-persistent but for different event types of Attribute launch point type records, OBJECTEVENT saves a different value in database table. For example,

  • for attribute launch point type records with event Validate, objectevent=0, 
  • for attribute launch point type records with event Run action, objectevent=1,
  • for attribute launch point type records with event Initialize Value, objectevent=2, etc.

So, if we keep the where clause for migration object DMLAUNCHPOINT as below, it might solve the above issue.

  • (launchpointname='launchpoint1' and objectevent=2) or (launchpointname='launchpoint2' and objectevent=1) or .....

This is not tried and tested, however, would like to know your suggestions/thoughts around this.