The basic building block of all ABAP OO for Workflow classes is the IF_WORKFLOW interface. Unless you use the IF_WORKFLOW interface in your classes, the workflow system will not recognise your class as suitable for creating workflow tasks, events, accessing attributes, etc. The class could be a new or existing class.
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.
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.
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.
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.
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.
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.
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.
Unknown User (zh6ygl9)
your article was very helpful! Based on it i was able to setup a workflow which is using my own ABAP OO Object (instead of a BOR Object) to provide attributes and methods.
In the past i often used the macro "exit_cancelled" in syncronous methos of BOR Objects. Is there something wich replaces this marco if i try to keep the work item in the inbox because it seems that i can not use this macro anylonger. The method exceptions are working fine.
My only idea for a work around is to make the method to a asyncronus method and define events which will finish the workitem. But i'd like to keep the syncronous task.
Do you have an idea?
You can use the temporary exception 'CX_BO_ACTION_CANCELLED' in your method. This is a work around for exit_cancelled used in BOR.
e.g. After all the desired code in your method raise this.
<call web form for approval>
RAISE EXCEPTION TYPE cx_bo_action_cancelled.