Getting started with ABAP OO for Workflow … using the IF_WORKFLOW interface
Note: This applies even to utility classes that implement static methods such as calculating deadlines based on factory calendars, de-duplicating a list of agents, or an empty background method for forcing a switch to the workflow system user.
What should I know before reading further?
If you’ve never created an ABAP OO Class before you won’t learn how to do that here. So you should have a look at the ABAP Development blogs and tutorials first, or read an ABAP Objects book.
Adding the IF_WORKFLOW interface to an ABAP Class
Attaching the IF_WORKFLOW interface to an ABAP Class is simple. In the Class Builder (transaction SE24), go to the Interfaces tab and add the IF_WORKFLOW interface. As soon as the interface is added, two sub-interfaces appear: BI_OBJECT and BI_PERSISTENT.
Move across to the Methods tab and you will see some methods of these interfaces have been automatically inherited to the ABAP Class.
What’s the absolute minimum I need to implement from the IF_WORKFLOW interface for a utility class?
Open each of the methods inherited from the IF_WORKFLOW interface, and activate the empty source code, then activate the ABAP Class itself. No, really – that’s it!
What’s the absolute minimum I need to implement from the IF_WORKFLOW interface for a class representing a business entity?
Open each of the methods inherited from the IF_WORKFLOW interface, and activate the empty source code, then activate the ABAP Class itself. That’s the easy bit, the next bit is only fractionally harder, but requires a little explanation.
Workflow uses the IF_WORKFLOW interface to generically and efficiently handle all references to ABAP OO for Workflow classes. There is a special part of this interface called the Local Persistent Object Reference, which is simply a way of converting from a generic reference to any object instance used by the workflow system to the specific instance of your ABAP Class and vice versa.
The instance is made up of three fields:
- CATID – A two character code indicating the type of object. This is always “CL” for ABAP Classes. I’ll explain about other values in a future blog on referencing BOR objects.
- TYPEID – This holds the technical name of the ABAP Class.
- INSTID – This is a 32 character field holding the unique instance id.
So what do you put in the INSTID field? Unless your ABAP Class is a utility class, usually the ABAP Class would represent a business entity of some kind. In the screenshots in this blog I’ve shown an example of a class that represents a Plant. To uniquely identify a plant I would use the 4 character Plant number, so for this class the plant number is what I would place in the INSTID field.
Similarly if I had a class representing a Sales Order, I would use the Order number to fill the INSTID field. If I had a class representing a Customer I would use the Customer Number. If I had a class representing a financial document I would concatenate the company code, document number, and fiscal year and use that as the INSTID value.
Note: It’s a good idea (although not essential) to mark the attribute you use to fill the INSTID value as the “key attribute” in the Class Attributes section. This helps other developers to quickly see which attribute uniquely identifies the instance without having to drill down into method code.
What happens if the class represents something that has a unique key longer than 32 characters? In this case you would use a Globally Unique Id (GUID) to represent your unique key, and then relate the GUID back to the real key. A GUID is unique throughout your system – for this reason a lot of the latest SAP solutions use GUIDs rather than meaningful ids as technical ids. To create the GUID, you simply call function module GUID_CREATE. To relate the GUID back to the real key, you could either cross-reference the GUID in a separate custom table, or use an append structure to extend an existing table to also hold a GUID reference.
It’s convenient to hold the Local Persistent Object Reference value in a public, instance attribute of your class. Workflow won’t use the attribute directly, but holding it as an attribute allows you to efficiently fill it once in the Constructor method, and can also be handy for debug and testing purposes. You can call the attribute whatever name you prefer, just make sure you use the data type SIBFLPOR.
To be able to reference a unique instance of a ABAP Class in workflow, you need to use the Local Persistent Object Reference in the following two methods inherited from the IF_WORKFLOW interface.
BI_PERSISTENT~FIND_BY_LPOR
This is a Static method that is used to convert from the Local Persistent Object Reference used by workflow to a unique instance of the ABAP Class. It’s used any time workflow needs to implicitly instantiate the ABAP Class, e.g. when raising an event.
data: lv_plant type werks. move lpor-instid(4) to lv_plant. create object result TYPE ZCL_PLANT exporting plant = lv_plant.
BI_PERSISTENT~LPOR
This is an Instance method that converts from the current instance of the ABAP Class to the Local Persistent Object Reference used by workflow. It’s used any time the workflow wants to pass a reference to the ABAP Class, e.g. when handling an event.
You can either fill in the Local Persistent Object Reference attribute here or preferably (as it is then filled once only per instance) in the Constructor method of the class.
* These 3 lines would normally be in the Constructor method me->m_por-CATID = 'CL'. me->m_por-TYPEID = 'ZCL_PLANT'. me->m_por-INSTID = me->plant. * If the 3 lines above are in the Constructor method, * the line below is all you need in the BI_PERSISTENT~LPOR method result = me->m_por.
What else can I use the IF_WORKFLOW interface for?
The other methods inherited from the IF_WORKFLOW interface are optional, but are useful in certain scenarios. It’s up to you to decide whether you want to use them or not.
BI_PERSISTENT~REFRESH
This method is used when workflow asks to refresh the object, i.e. reload database and other stored values. You can use it to clear out and re-read existing attributes that were filled in your Constructor and Class Constructor methods, but most of the time it’s ok to leave this method empty.
BI_OBJECT~DEFAULT_ATTRIBUTE_VALUE
This method is used in workflow inboxes, such as the Universal Worklist, and workflow administration tools to indicate which unique instance of the class is being referenced. By default, whatever value is in the INSTID field of the Local Persistent Object Reference will be shown. If you want to show a different attribute, just add the relevant code.
For example, if I have a class representing an instance of a Plant, then the key might be Plant Id, but I want to show the attribute “Description” instead, so I’ll add the line:
GET REFERENCE OF me->description INTO result.
BI_OBJECT~EXECUTE_DEFAULT_METHOD
This method is used in workflow inboxes, such as the Universal Worklist, and workflow administration tools, to control what happens when you drill down on the instance of the class being referenced. By default the drill down does nothing. If you want the drill down to call a specific method, typically a display method, then just add the relevant code.
For example, if I have a class representing an instance of a Plant, then I might want the default method to be the display method so I’ll add the lines:
TRY. CALL METHOD me->display. CATCH cx_root. ENDTRY.
BI_OBJECT~RELEASE
This method is used by the garbage collector when deleting an instance. You could use it to, for instance, write to a custom log, but most of the time it’s ok to leave this method empty.
Can I call the IF_WORKFLOW methods directly from a workflow task?
Even though the IF_WORKFLOW methods are public methods, they are intended to only be used internally by the workflow engine, and so no, you can’t call them directly from workflow (you’ll get an error if you even try). So that’s what the next blog will be about – how to use an ABAP Class in a workflow task.
Thanks,
Ramki.
Jocelyn
Very interesting article - eagerly waiting for your next blog on the topic
In the mean time i have a doubt - in the standard BOR we have some standard business objects available for Customer (KNA1) , Vendor (LFA1) etc - we can create sub types and use the standard methods and attrubutes and then add our own methods - how can we do the same using ABAP OO classes - do we have such standard classes availble?
Thanks & Regards
Sushmita
Later in the series I'll show you how to link ABAP OO and BOR object types. Yes there isn't much business content for workflow yet in ABAP OO mode (HR Life and Work events were the first) - but you can still use ABAP OO to extend BOR objects. From recent experience on projects, this approach is preferable to using BOR subtypes - e.g. because ABAP OO developers can understand most of what's going on.
Regards,
Jocelyn
thanks for your superb blogs, they help me a lot!
Anyway, I have a question relating the CATID: You say that for ABAP classes its always 'CL'. But when I drill down to the possible values for the domain, it says that 'CL' is for internal use only and that 'BC' should be used (on 4.7 Enterprise). What's correct?
Greetings from Germany
Joachim
Thanks for the encouragement and glad you are finding the blogs useful! As for your question, CATID = 'CL' is correct.
'BC' is for something entirely different ... 'Business Class' which is part of the Object Relationship Service - a way of relating objects in a process together. I don't know much about this as yet, but it would appear to be more of a modelling and process tracking mechanism. In any case it won't help you with ABAP OO for Workflow for which you definitely need CATID='CL'.
The reason you get the warning is because you are on R/3 4.7 Enterprise - which is based on SAPNetWeaver 6.20. As I mentioned in this article, there are a couple of pieces of the OO/Workflow puzzle missing in 6.20 so officially ABAP OO for Workflow is available for general use from 6.40.
However so long as you are aware of the restrictions (e.g. no functional method use), I haven't come across any major problems using ABAP OO for Workflow in a 6.20 environment, and I have used it successfully on 6.20 workflow projects.
Regards,
Jocelyn
Thanks for that. I’m trying to raise an event from a sales user exit.
In MV45AFZZ the method cl_swf_evt_event is called and the object type, event, objkey and obj cat is exported.
Object Type = ZBUS203200
Event = Z_TRAD_ORDER_CHANGE_OO
I created my class ZCL_UPDATE_PUR_REQ (by copying CL_SWF_RUN_WIM_HANDLER
And using interface name BI_EVENT_HANDLER_STATIC )
In /nswetypv I assigned Class ZCL_UPDATE_PUR_REQ
to Object Type ZBUS203200
And Event Z_TRAD_ORDER_CHANGE_OO.
All that works fine except for passing in the objectkey.
In Class ZCL_UPDATE_PUR_REQ
Method BI_EVENT_HANDLER_STATIC~ON_EVENT
When I go to create a parameter for object key, I get the message
‘Parameters/exceptions of inherited methods or events cannot be changed’.
Has you for how I can get the object key into the method call BI_EVENT_HANDLER_STATIC~ON_EVENT?
Thanks
Hi Jocelyn,<br/>I am doing OO ABAP for the first time and I needed to use it in the workflow. Your knowledge was indeed extremely helpful.<br/><br/>I need some important and urgent help from you:-<br/>----
Vinod
This is why generally its best to use e.g. BAPIs ratther than BDC.
But to get it solved I suggest you put some details of your problem and how you are doing the material master update on the BPM forum. There are lots of folk there to help you sort it out.
Regards,
Jocelyn
Your blog is very easy to understand and comprehend. However, I was unable to determine how to use GUID as the object key in cases where the actual key exceeds 32 characters. I am in a situation where my actual key is a combination of 2 fields, totalling 80 characters. I'm unable to determine how to establish the link between the GUID and the actual Key and also, whether to declare only GUID as the key or the two attributes as key as well. Any guidance would be highly appreciated.
Thanks!
Mohammad.
Thanks a lot Jocelyn for sharing this 🙂
Thanks Vishai!
Hi Jocelyn,
Thanks a lot for putting all these things together,thanks for all your effort in this.This is very helpful for those who want to implement workflow using ABAP OO Class and this blog covers all the basic things which needs to be followed in this kind of implementation of workflow using ABAP OO Class.Thanks again!!!
Thanks
Hafizul
Glad you liked found it useful!
Hi Jocelyn,
I am very much new to Workflow. After reading your blog about using ABAP OO for workflow, I have developed a workflow myself now using ABAP OO to meet a simple business requirement.
It is really useful for beginners like me. Keep posting.
Thanks,
Sankar Ram S.
Thanks Sankar - good to hear it's still useful!
Yes it is.. 🙂 Helped me to move from BO based to ABAP Class Based approach . Thanks Jocelyn.
Hi Jocelyn,
Thanks for sharing the documents, I have two question.
1. We need to implement the workflow for PA Infotypes table, the Key field for PA infotypes in 34 Characters. But SAP class based WF support only 32 characters. We have multiple workflow. So how we can proceed for this. So as u suggested create Custom table but i think so its difficult to maintain the table for multiple infotype.
2. In HR workflow, for customized WF we need to maintained the entry in SWEHR3 Tcode. There is a field for the Business object. But we don't have any field for the Classes.
Thanks and Regards,
Nishant Bansal
Hi Nishant,
1. For anything longer than 32 characters you need to create a GUID and map it to the actual data key you are using.
2. It is possible to use ABAP Classes for PA infotypes. You need to use the Function Module option in SWEHR2 to trigger ABAP Class events instead of BOR events. This has been done at other sites and is actually fairly straightforward.
Rgds,
Jocelyn
In simple cases I have just left some of the key fields of PA out from the class key. For example if you leave out field OBJPS (length 2), you have 32 characters left. Of course you need to be sure that the fields do not have any role in your business process & workflow now and in the future (~the field is always empty).
Of course if you have multiple workflows/classes and/or there are strict requirements for the key, it makes sense to build a GUID based solution, which you can then utilize everywhere and be absolutely sure about the robustness of your object key.
Kind regards,
Karri