on 12-17-2012 1:14 PM
I have an issue using ABAP classes in workflow. I just can't see why some attributes lose their values.
The workflow handles an invoice. A custom ABAP class represents the invoice in the workflow container.
I pass the invoice object from the wf to the task in the binding. In the task container, I have a container element CO_INVOICE that matches the method's CHANGING parameter CO_INVOICE. The method does something, then sets the invoice object's public STATUS attribute to PARKED. When I look at the results from wf log, I see the object's STATUS attribute is initial. When I test the method directly in SE80, without using the wf, the status is correctly set to PARKED. It looks as if wf loses the attribute's value. However, the key attributes (invoice number, fiscal year) are correct.
We're running 7.02.
One thing I noticed was that when I view the contents of the container elements in the workflow log, the objects get reinstantiated. That way, the log does not necessarily correspond to the runtime values. However, I find that I can't rely on the instance attribute values in the workflow, because the workflow only stores the LPOR to the container, and any other attributes are transitory.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Yes that is what happens when you use the workflow log. If you do not have persistance, the values you see in the container are the ones initialized by the WF runtime. The methods in IF_WORKFLOW are not only meant for initializing values but also for retrieving existing values from the persistance layer.
Well, I guess it's case closed then.
So sum it up, I can't pass values from the method to the workflow using object attributes, unless I implement persistent objects that explicitly save the values in the database. If I just pass the object reference (of a transient object), the object might get reinstantiated, so any attribute values would be lost.
OK, it figures. The wf engine creates and discards objects at its own pace, and only the LPOR is actually stored in the container. I was hoping I could pass the object like you'd pass an object to a method in a normal ABAP program. In ABAP, you'd be pretty disappointed if attribute values were lost while the program is running. But in wf, that's life...
It's actually not just "that's life" it's about the performance and intention of workflow. Remember that workflow is most of the time acting asynchronously so that its actual runtime and hence load on the system is very very small. That would not be the case if you were passing large quantities of data around and hanging on to them all the time. So any stored data is meant to be stored in the database and the ABAP Class for workflow is used to access that data only as and when needed. Remember also most ABAP programs run for a comparatively short period of time - generally seconds or minutes. Whereas workflows can run for hours, days, weeks, years... and there's a lot of time when all it would be doing is tying up dialog or background processes waiting for someone to go to their inbox if it didn't take this approach to the persisistency of objects.
Hope that helps
Regards,
Jocelyn
Hi Jocelyn,
I have an below issue.
How to know Called workflow name at runtime from Class Method
Can it be resolved by using these methods ?
Thanks,
Manoj
Hi Arto, Just looking at your code, including find_by_lpor inside your get_instance method is not recommended. A better approach would just be to use a CREATE OBJECT statement at that point instead. Or if you want to have some reuse of code, have a private method that is called from both your FIND_BY_LPOR and GET_INSTANCE methods.
There's a certain point when it can all get rather confused if you have too many cyclical calls.
It's best to leave the find_by_lpor and lpor methods for direct use by the workflow system and not make your own additional methods dependent on them.
I wrote a series of blogs on this back in 2006 that are still good - if you follow the approaches recommended you should have fewer issues. Here's a couple of them to check against.
Regards
Jocelyn
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi Jocelyn,
sure, I've read, bookmarked and re-read your blogs. They're true gems
Still, it somehow escaped me that I shouldn't use any IF_WORKFLOW methods in my own code. I thought "call them directly" referred to using them in tasks.
Anyway, I now replaced the call with a direct CREATE OBJECT, but it didn't help retaining the attribute values.
Perhaps the only way to keep those attribute values all through the call stack is indeed using persistent objects. In this case, I just added simple character type parameters to the interfaces and use them in the bindings. It does the trick, but to me, it's not as elegant as using objects.
Hi,
Check in the constructor or find by lpor method, this attributes are not initialized. also share the screenshot of the class.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi,
in the method GET_INSTANCE, setting the attribute is the very last thing the method does before returning the object. So constructors etc. shouldn't get in the way. The code and a picture of the class are below.
The matter seems to be related to static vs. instance methods. GET_INSTANCE has to be static, as it creates the object for the workflow. Subsequent methods, such as CHANGE can be instance methods. I tried both versions, and when using an instance method, the status is retained.
All in all, why are there several identical elements in the task container? Firstly, there's CO_INVOICE, which was automatically created from the method parameters, according to wf engine's suggestion. Secondly, there's _Wi_Object_ID, which is also automatically created. Thirdly, in some tasks there's also the element _WI_RESULT, which refers to the same object class. All three refer to the same class, and the methods I call are similar. Curiously, the _WI_RESULT can't be seen in PFTC, but only when looking at the task container in SWI1 log.
When I want to use an instance method, I have to pass the instance to _Wi_Object_ID in the task, or else I get a null pointer error. However, I must also pass the same instance to CO_INVOICE, so it is transferred to the method.
(I tried making an explicit binding from _Wi_Object_ID in the task to CO_INVOICE in the method, but that wasn't allowed. The error message reads 'The object type ZIF_...' is not (actively) available.).
Point being: what's the purpose of all those task container elements? What's the best way of passing the instance to the method?
METHOD get_instance.
DATA ls_lpor TYPE sibflpor.
DATA lv_inst TYPE sibfinstid.
IF ( NOT i_document_number IS INITIAL )
AND ( NOT i_fiscal_year IS INITIAL )
AND ( NOT i_company_code IS INITIAL ).
ls_lpor-catid = 'CL'.
ls_lpor-typeid = '/BILOT/CL_IV_AP_INVOICE_FI'.
CONCATENATE i_company_code i_document_number i_fiscal_year INTO ls_lpor-instid.
r_invoice_fi ?= /bilot/cl_iv_ap_invoice_fi=>bi_persistent~find_by_lpor( ls_lpor ).
r_invoice_fi->/bilot/if_iv_ap_invoice_fi~set_status( 'NEW' ).
ENDIF.
ENDMETHOD.
METHOD get_instance.
DATA ls_lpor TYPE sibflpor.
DATA lv_inst TYPE sibfinstid.
IF ( NOT i_document_number IS INITIAL )
AND ( NOT i_fiscal_year IS INITIAL )
AND ( NOT i_company_code IS INITIAL ).
ls_lpor-catid = 'CL'.
ls_lpor-typeid = '/BILOT/CL_IV_AP_INVOICE_FI'.
CONCATENATE i_company_code i_document_number i_fiscal_year INTO ls_lpor-instid.
r_invoice_fi ?= /bilot/cl_iv_ap_invoice_fi=>bi_persistent~find_by_lpor( ls_lpor ).
r_invoice_fi->/bilot/if_iv_ap_invoice_fi~set_status( 'NEW' ).
ENDIF.
ENDMETHOD.
Hi Arto, long time no see
What you are seeing might be a side effect of how workflow instantiates objects. Your container might not get updated even though the method is executing. What is your persistency? Can you check whether the changes have been made to the persistent object? You could place breakpoints in all the affected methods to find out when the object gets instantiated and what methods are being called and when. One other thing is that you should always define attributes as Import & Export if you make changes to them and make the mapping bi-directional between the workflow container and the task.
Hi Samuli,
I agree, I think this is a matter of instantiation and persitance. While debugging, I see that the attributes are set in the Z-code, and the values are still there when I get back to SAP's standard code. However, they never reach the container.
The original object probably gets lost, then the workflow calls FIND_BY_LPOR, which in turn calls the constructor and sets the 3 key attributes (document number, fiscal year, company code). That explains why only those attributes have values, but others lose them.
I'm not using persistent objects, but now it seems I'd have to, in order to keep the attribute values. I think that's a flaw in the wf engine, because it'd seem logical to just pass the same object on in the bindings.
The import & export checkmarks haven't posed me problems before, and I have a feeling they are rather informative than required. But yes, I should use them consistently. Now they are in place, but with no effect. The object ref did get through all the time.
method BI_PERSISTENT~FIND_BY_LPOR.
data: lv_invoice_number type belnr_d,
lv_year type gjahr,
lv_company_code type bukrs.
*- begin of local data
DATA: l_invoice_fi TYPE REF TO /BILOT/CL_IV_AP_INVOICE_FI.
DATA: l_instance TYPE t_instance.
DATA: l_typename TYPE seoclsname.
*- end of local data
CHECK lpor-instid IS NOT INITIAL.
lv_invoice_number = lpor-instid+4(10).
lv_year = lpor-instid+14(4).
lv_company_code = lpor-instid(4).
*- set typename
IF lpor-typeid IS INITIAL.
l_typename = '/BILOT/CL_IV_AP_INVOICE_FI'.
ELSE.
l_typename = lpor-typeid.
ENDIF.
*- instid is the key of the object
READ TABLE mst_instances WITH KEY number = lpor-instid
INTO l_instance.
IF sy-subrc <> 0.
TRY.
CREATE OBJECT L_INVOICE_FI
EXPORTING
COMPANY_CODE = lv_company_code
INVOICE_NUMBER = lv_invoice_number
FISCAL_YEAR = lv_year
.
CATCH cx_bo_error .
*------ object not found
EXIT.
ENDTRY.
l_instance-number = lpor-instid.
l_instance-instance = l_invoice_fi.
APPEND l_instance TO mst_instances.
ENDIF.
result = l_instance-instance.
endmethod.
User | Count |
---|---|
94 | |
11 | |
10 | |
9 | |
9 | |
7 | |
6 | |
5 | |
4 | |
4 |
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.