cancel
Showing results for 
Search instead for 
Did you mean: 

How to have a general task workitem only show up in one specific user's inbox?

BaerbelWinkler
Active Contributor
0 Kudos

As all good things come in threes (or so I'm told!) here is a third question related to a simple workflow I'm trying and having a hart time to implement. The earlier questions were "How to identify what the actual error is when a new workflow doesn't work when tested in SWDD?" and "How to switch the default priority of a Workflow to "1" and to then get the express-notification?"

The gist of what I set up is available here and it almost does what I'd like it to do. But one issue is basically a showstopper because each created workitem is not just visible in the intended recipient's inbox but in everybody's:

So User 1 sees all four of them as does User 2 and even User 3 who I didn't even trigger the work item for gets to see them as well and could potentially process them.

As this mini-Workflow is intended to get new developers to actually look at our development guidelines and then "sign off" by using the "Confirm task" button, it needs to go to exactly the developer it's intended for, anything else doesn't make much sense.

As outlined in the workflow book's chapter, I defined the task as general:

Reading up on "general tasks" in the book, I get why this then causes the workitem to show up in everybody's inbox, but I thought that the expression in the task definition should take care of that:

The code for ZCL_USER is copied from the information provided in the Workflow book. To not make this post too long, I put it into the attached txt-file:

utility-wf-user-determination-zcl-user.txt

In my wrapper-program I trigger the work item creation via FM SAP_WAPI_START_WORKFLOW and already tried with either just the user-ID or with it prefixed by "US" but the result is the same:

    clear lt_container_simple.

    ls_container_simple-element = 'ACTUAL_AGENT'.
    CONCATENATE 'US'
                i_agent
           INTO ls_container_simple-value.
*    ls_container_simple-value   = i_agent.
    APPEND ls_container_simple TO lt_container_simple.

    ls_container_simple-element = 'USER'.
    CONCATENATE 'US'
                i_agent
           INTO ls_container_simple-value.
*    ls_container_simple-value   = i_agent.
    APPEND ls_container_simple TO lt_container_simple.

    CALL FUNCTION 'SAP_WAPI_START_WORKFLOW'
      EXPORTING
        task            = p_task
        language        = sy-langu
        do_commit       = abap_true
        user            = sy-uname
      IMPORTING
        return_code     = lv_returncode_wfl
        workitem_id     = lv_workitem_id
        new_status      = lv_new_status
      TABLES
        input_container = lt_container_simple
        message_lines   = lt_messages_wfl.

The result looks okay judging what my wrapper program tells me (I switch the prio of the work item to 1 and also trigger an express message):

The container information of the work items look okay as each shows the expected user-IDs in the relevant fields:

I have both container elements filled in the binding via the same expression (one of my trials was to actually add a line for USER even though this isn't mentioned in the book - it didn't seem to have a negative effect but it also didn't solve my issue)

So, what am I still missing?

Thanks much and Cheers

Baerbel

Accepted Solutions (1)

Accepted Solutions (1)

pokrakam
Active Contributor

OK I see the problem. The agent field is a flat type, it needs an OM object ID structured as <objtype><objid>, so 'US' for user and the userid. In your binding you're passing the user object. Ideally SAP should be giving a warning or error, but it seems to be ignoring it completely, in which case it's (correctly) sending it to all possible agents.

Technically speaking you don't need the user class at all, you could just start the WF with US<userid> as the recipient. I use an object because more user functionality is almost always needed, if only just to write "Dear <fullname>".

Suggest adding an attribute wf_agent_id or a functional method get_wf_agent to the user class that returns USERNAME prefixed with US. Then place that into the agent field.

The part with Actual_agent is in the book only because that's a very common scenario. I also wanted to demo how easy it is to instantiate an object in the binding. So a task returns "USABC" and the WF receives an object instance. In a typical WF a followon step might then send an email stating "Your <blah> was approved by &ACTUAL_AGENT.GET_FULL_NAME()&"

Hope that makes sense.

BaerbelWinkler
Active Contributor
0 Kudos

Thanks for following me to this latest thread, Mike!

I'm afraid that it would be quite an overstatement if I said that your answer makes sense to me. I think I get the gist of it, but that doesn't immediately translate into knowing what I'll have to do once I'm back in the office tomorrow. And to state the obivous: this is all due to me still knowing and understanding next to nothing about workflows (and OO for good measure) and therefore getting confused by all the terminology and what has to be done where (in the class? in which method? in the task-definition? in the binding? in my wrapper program? in one of the many tabs? somewhere else?).

I think what I'll need to do is to start almost from scratch by commenting out all the potentially not even needed code in the class. I for example don't need any data about the user as in a lot of cases, this logic will be used to prompt the pop-up for a generic user assigned temporarily to a consultant/developer where no actual name is available in SAP anyway. So, one of my questions is: do I even need any of the lpor-related logic or can that all be safely commented out?

My wrapper program has logic to for example check that the selected user-IDs have the developer role, so there's no need for such a check within the workflow or class definition. My preferred solution would be one, where I just feed the needed information in the required format via the FM SAP_WAPI_START_WORKFLOW and the work-items get created to only show up in the selected users' inboxes (one can dream, right?!?).

Cheers

Baerbel

BaerbelWinkler
Active Contributor
0 Kudos

New day - continued confusion on my part ....

I'll copy and paste from your answer and ask what I'm not clear about:

"In your binding you're passing the user object."

==> What if anything of the binding do I need if this process will always be triggered from my wrapper program? I just got rid of the binding from Step to Workflow completely and the result from my wrapper program looks the same as before. The tasks for two users get created and still show up in all inboxes.

".... you could just start the WF.."

==> am I actually starting the WORKFLOW when I trigger the task from within my wrapper program with SAP_WAPI_START_WORKFLOW as - the FM's name notwithstanding - it expects the TASK as an input parameter?

"Suggest adding an attribute wf_agent_id or a functional method get_wf_agent to the user class that returns USERNAME prefixed with US ...."

==> I tried to see what of the class and its methods gets currently executed when I trigger the process via my wrapper program but I couldn't get the debugger to actually stop at the breakpoints I set. So, I'm not sure if they are just executed in another session or if they are not even accessed. I also just now tried via SAT but that also doesn't show any hits for ZCL_USER. The breakpoints also don't have an effect when I try via SWUS_WITH_REFERENCE.

==> I'm also still not sure how to actually feed the intended recipient's user-ID into the task (apart from what I'm already trying to do in my wrapper program by filling LT_CONTAINER_SIMPLE which seems to work as the container fields get filled as expected - but also doesn't work as it doesn't restrict who actually gets which work-item(s)

==> I changed the definition for USERNAME (in its various incarnations) in ZCL_USER from TYPE XUBNAME to SWP_INITIA which is what shows up as the underlying definition for _Wf_Initiator and is based on domain HROBJEC_14 with the description of "Concatinated identification (type and ID) of org. objects". That should be the proper format, right?

".... Then place that into the agent field."

==> I don't really know what/how/where I need to do this, especially as I already have two fields USER and ACTUAL_AGENT in the container definition. Would one (or both) of them simply need to be differently filled via another method in ZCL_USER so that the expression in the binding from TASK to WORKFLOW %ZCL_USER.GET_INSTANCE_FROM_WFAGENT(I_WFAGENT=&_WI_ACTUAL_AGENT&)% just needs to be replaced by however I call things in the class? Or am I still missing the point and I need yet another container element?

Sorry for all those questions which are obviously an ongoing indication of my cluelessness and that I perhaps shouldn't have started to dabble in workflow to begin with.

pokrakam
Active Contributor

I'm a bit short on time right now, so will just give a shortish answer. Might try find a bit of time for a short blog with demo later.

The good news is that you are overthinking it and trying to make things more complicated than they are. Your container element USER is type object (TYPE REF TO in ABAP equivalent). What is needed is a CHAR containing the US-prefixed ID, "USABC".

Think of the binding as the parameter part of a method call. You're doing EXPORTING lo_user instead of EXPORTING lo_user->get_name_with_us_prefix( ). The agent SWO_AGENT, or domain HROBJEC_14, or any flat char field. What I suggested for simplicity is that you could drop the class altogether and just work with the HR Object key "USABC".

But it's a good thing to have the class. I also don't agree with stripping out the functionality, quite the opposite, I think this is a perfect use for OO. So my trigger program would have:

if user->is_developer( ).

to perform the role check.

BaerbelWinkler
Active Contributor
0 Kudos

Thanks for bearing with me, Mike!

I tried to "translate" your reply but don't think that I really got it. Here is what I did:

  • switched the definition of the WF container element USER from ZCL_USER to ABAP Dict. Data Type "HROBJEC_14"
  • copied the static public method GET_INSTANCE_FROM_WFAGENT to GET_HRUSER_FROM_WFAGENT which then has R_USER_HR as a returning parameter of type SWP_INITIA. This gets filled via a concatenation of "US" with the usernmame
    * <SIGNATURE>-----------------------------------------------------------------------+
    * | Static Public Method ZCL_USER=>GET_HRUSER_FROM_WFAGENT* +------------------------------*-------------------------------------------------------------------+
    * | [--->] I_WFAGENT  TYPE  SWHACTOR
    * | [<-()] R_USER_HR  TYPE  SWP_INITIA
    * +--------------------------------------------------------------------------</SIGNATURE>  method GET_HRUSER_FROM_WFAGENT.
    "Create instance using WF agent structure
     DATA lv_uname TYPE SWP_INITIA.

    "Handle different or object types CASE i_wfagent-otype. WHEN 'US'. lv_uname = i_wfagent-objid. CONCATENATE 'US' lv_uname INTO r_user_hr. WHEN OTHERS. ENDCASE. endmethod.
  • changed the binding so that &USER& gets filled via %ZCL_USER.GET_HRUSER_FROM_WFAGENT(I_WFAGENT=&_WI_ACTUAL_AGENT&)%

When I then check the workflow I get an error for the binding:

'GET_HRUSER_FROM_WFAGENT' is an instance method. Call-up only possible using object instance
Message no. SWF_EXP_001179

Why does this message imply that the new method is an instance method as it's defined as a class-method? I did find an OSS-note for this message but it's from 2008 (Basis 711) so shouldn't be relevant for our system (Basis 750).

 class-methods GET_HRUSER_FROM_WFAGENT
importing
!I_WFAGENT type SWHACTOR
returning
value(R_USER_HR) type SWP_INITIA .

And a definite "yes" to overthinking this and thereby making it more complicated than it actually is - I "just" have a hard time to see the big picture with all the various bits and pieces of this particular puzzle.

pokrakam
Active Contributor

No, you got it almost right but completely the opposite 🙂

You can do either one of two things:

1. Use the flat field/structure throughout. &USER& then must be defined as a flat type and becomes just the ID and gets filled with 'USDRACULA' in the SAP_WAPI_START_WORKFLOW container parameter. You can hardcode an ID in the WF builder step (agent field) to test it.

2. Use an object instance in your WF container. This is what you have done, &USER& is of type Object. This is usually a better choice as you can do much more with it. BUT, the workflow expects an HR descriptor in the agent expression, so whatever you place in that field in the workflow builder must evaluate to 'USDRACULA'. So if you have an object instance in your WF, use the binding exactly as per your above post. This tells the WF system to execute that method and use the result as your agent.

(As an aside, the US is necessary because it can be many other HR object types, such as O for Org Unit or AC for role, and this would then route to multiple people).

BaerbelWinkler
Active Contributor
0 Kudos

I'm still not sure I get what I need to do 😞

So, let me try to put my tentative understanding into words for your two options:

1. I just switch the container-definition for USER to ABAP Dict. Data Type of e.g. HROBJEC_14

As that then just gets filled from my wrapper program via the FM I don't need anything in the binding definition (or, if I do need something, I don't really know what that might be and how it needs to be spelled out).

When I then run my wrapper program for two users the work items get created as before (with USUserID as the User), but they still end up in everybody's inbox (and one of my colleagues who I didn't even trigger them for just cleaned them out completely for everybody in the sandbox). So just having the user in the expected format doesn't seem to prevent that. What else could be missing? As long as everybody gets/sees these work-items I cannot use them for the intended purpose.

2. I leave the container-definition for USER as CL.ZCL_USER and have the following statement for the binding:

&USER& <-- %ZCL_USER.GET_HRUSER_FROM_WFAGENT(I_WFAGENT=&_WI_ACTUAL_AGENT&)%

I have the returning parameter for that method defined as:

R_USER_HR TYPE SWP_INITIA

It gets filled as a concatenation of "US" with the provided user-ID

With that definition, I get an error on the binding:

"An object compatible with '[CL.ZCL_USER]' was expected as the source expression
Message no. SWF_BND_001074"

So, how do I make both the binding and the subsequent step happy? At the moment this feels like a catch-22 where the step expects an HR-object and the binding can only provide a class-object. But as long as the work items end up in everybody's inboxes as per above, getting this to work via the class is not really my highest priority. For now, I'll therefore go with option #1 and will not spend more time on making this work via a class object (the chances are close to zero anyway in our current setup that this class would be used for anything else anyway).

pokrakam
Active Contributor

Sorry about delayed reply. You are 99% there!

2. I leave the container-definition for USER as CL.ZCL_USER and have the following statement for the binding:

&USER& <-- %ZCL_USER.GET_HRUSER_FROM_WFAGENT(I_WFAGENT=&_WI_ACTUAL_AGENT&)%

The method does not make sense, you're getting a US-prefixed ID and putting the US in front of the ID part, effectively returning your input but in a different datatype.

I wasn't aware you had a requirement to also use the 'actual agent' in a subsequent step. In an OO WF you want to:

- Put a flat US-prefixed ID into your Agent field: %USER.GET_WFAGENT( )%

- WF returns a flat US-prefixed ID. Here I would create a second object ACTUAL_AGENT also of type ZCL_USER

- This is where you can use the return binding to instantiate the actual agent:

&ACTUAL_AGENT& <-- %ZCL_USER.GET_INSTANCE_FROM_WFAGENT(I_WFAGENT=&_WI_ACTUAL_AGENT&)%

BaerbelWinkler
Active Contributor
0 Kudos

Thanks for your patience, Mike! By now you must think that I'm rather dense, but - believe it or not! - this isn't usually the case. But this workflow and OO-"stuff" gets me each and every time even if just one of them is involved, so you can imagine what the combination of it does!

Considering that you think I'm 99% there I still have way too many items I don't undertand, namely where I need to actually put what. As I'm rather visually inclined I created a "composite" screenshot with numbers so that I know where you indicate I need to have something and what.

I'm especially interested to learn where and how to actually "Put a flat US-prefixed ID into your Agent field: %USER.GET_WFAGENT( )%"

(1) Do I need any other container element apart from "User" defined as shown at (5) and "Actual_Agent" defined as "CL ABAP Class ZCL_USER"?

(2) Does anything need to be put in "Workflow --> Step" and if so what? I tried with %USER.GET_WFAGENT( )% but that just got me an error in the binding.

(3) This is technically okay, i.e. not throwing any errors but I don't really see where it gets used, if at all?

(4) Regardless of whether I put &User& or &Actual_Agent& there, I get an error in agent determination when I test this in SWDD:

How can this be for a general task? Only when I work with &User& in the expression and provide my userID with "US" as the prefix in the Input Data, it seems to work from within SWDD, at least I don't get the error-message.

However, when I trigger the work-items from my wrapper-program, they still end up in everybody's workflow inbox. Is this a bug or a feature in the FM SAP_WAPI_START_WORKFLOW?

And a different question: I started to write up this ongoing "saga" in a blog post over the weekend and am wondering whether to publish it now, without a "happy ending" or wait until this works like I hope it eventually will? Any thoughts about that?

Cheers

Baerbel

pokrakam
Active Contributor

OO isn't that magical. If it helps, just think of text field vs object as the equivalent of using a text field vs a deep structure. You need to specify the element of the structure in order to assign it to a text type variable and vice versa.

If your USER element is an object type (currently isn't in your screenshots), then number 4 is the one that needs to dereference the &USER& object into an HR agent. That's where you put &USER.GET_WFAGENT_( )&, you can even do this via the dropdown dialog if USER is of type ZCL_USER.

The "actual agent" is only used AFTER execution and therefore only needed for subsequent processing. I'm not sure if you need it. e.g. you might send an item to Mrs Smith, but they could forward it or have a substitute set up. So you'd capture ACTUAL_AGENT in your binding back from task to WF in order to capture who really executed it so you could then send an email to the requestor to say "Your Purchase Order was approved by &ACTUAL_AGENT.GET_FULL_NAME( )&". I didn't think this was relevant in your scenario.

BaerbelWinkler
Active Contributor
0 Kudos

OO may not be magical but it for sure is mysterious to me 🙂

Okay, so I switched (1) "User" back to ZCL_USER and it looks as if I can delete the binding in (3) as I'm not planning to do anything further with the workitem once it got processed/executed.

When I then use F4 on the expression in (4) I see these options:

So, there seems to be something missing in ZCL_USER which most likely goes all the way back to me not understanding what you meant with "Suggest adding an attribute wf_agent_id or a functional method get_wf_agent to the user class that returns USERNAME prefixed with US. Then place that into the agent field." in your very first comment. It's IIRC what I tried to implement with GET_HRUSER_FROM_WFAGENT but apprently got wrong.

pokrakam
Active Contributor

"It's IIRC what I tried to implement with GET_HRUSER_FROM_WFAGENT but apprently got wrong."

OK, that's it! Pick your GET_HRUSER... method. It doesn't need any importing parameters because the object instance already has USERNAME as one of it's attributes. So just one line of code:

r_result = 'US' & username. 

I should have spotted it but was confused by your use of actual agent and importing parameter.

Edit: Or if you want to use constants, there's a whole bunch in type group SWFCO. Thus:

r_result = swfco_org_user & username. 
BaerbelWinkler
Active Contributor
0 Kudos

Still no luck ....

I changed ZCL_USER as follows:

Definition - Public section

 methods GET_HRUSER_FROM_WFAGENT
 returning    
 value(r_result) type SWP_INITIA .

(is the type correct for this?)

Implementation

method GET_HRUSER_FROM_WFAGENT.
CONCATENATE 'US'
username
INTO r_result.
endmethod.

(At first, I got a syntax-error when I tried with "&", so I went with CONCATENATE instead)

The class generated okay.

Then I picked GET_HRUSER_FROM_WFAGENT from the F4-Help for (4) which then looks like this on the screen &USER.GET_HRUSER_FROM_WFAGENT()& and the check didn't produce any errors.

But, when I tested it via SWDD, no work-item was created as the process ended with this error:

Error in the evaluation of expression '<???>&USER.GET_HRUSER_FROM_WFAGENT()&' for item '1'

Message no. SWF_EXP_001072

pokrakam
Active Contributor

If you test via SWDD and just execute, USER won't have an instance and there's no method to run. In the initial test screen you should see "<no instance>" as its value. Hilight the element and you should be able to enter a userid under "Instance ID" at the bottom of the screen. Then hit enter and the value column should now show your ID. Now you can execute it with your chosen ID.

BaerbelWinkler
Active Contributor
0 Kudos

Thanks, Mike! Testing via SWDD now works.

But, when I trigger the workflow task via my wrapper program, the work-items are still visible in everybody's inbox.

This happens regardless of whether I feed in just a plain user-ID or one prefixed with "US":

 clear lt_container_simple.
ls_container_simple-element = 'USER'.
CONCATENATE 'US'
i_agent
INTO ls_container_simple-value.
* ls_container_simple-value = i_agent.
APPEND ls_container_simple TO lt_container_simple.
CALL FUNCTION 'SAP_WAPI_START_WORKFLOW'
EXPORTING
task = p_task
language = sy-langu
do_commit = abap_true
user = sy-uname
IMPORTING
return_code = lv_returncode_wfl
workitem_id = lv_workitem_id
new_status = lv_new_status
TABLES
input_container = lt_container_simple
message_lines = lt_messages_wfl.

Would there be a means to at least add the intended recipient's user-ID to the work-item title to make that explicit? I see that there's an "Insert variable" button available but none of the options seem to fit:

If that were possible than the work items would at least be clearly identifiable and not look the same as they do now:

But, considering the large number of users we have in the actual dev-systems, the work-items just shouldn't show up for everybody as that'll cause confusion sooner rather than later.

pokrakam
Active Contributor

Yes, we're back to the instance vs flat data bit. You are passing a flat ID into the container, but it's defined as an object instance in the WF. Because you're bypassing the 'usual' WF event mechanism, the explanation is a little more complicated than it needs to be, so I'll just give you the code:

data(user) = zcl_user=>get_instance( i_agent ).
append value #( element = 'USER'
                value   = user->bi_persistent~lpor( ) )
       to lt_container_simple.

This provides the "Local Persistent Object Reference", a special identifier by which the WF system can instantiate your object.

BaerbelWinkler
Active Contributor
0 Kudos

Thanks for the code, Mike!

After copying it into my wrapper-program I had to correct the typing for some parameters/fields (syntax-check wasn't happy when they were typed as SWP_INITIA but was with XUBNAME which is also consistent with what you show in the book for the various class-attributes for username-parameters). I eventually got it to work and the container-element for user in the created work-items now looks like this:

Is the user w/o the "US"-prefix what is expected or should it actually have it? I re-read the relevant parts in the book and compared my code to that, and it looks the same. If I haven't overlooked it, I didn't see anything where either one of the parameters needed to be typed differently or where there's a concatenation with "US" happening. But then, it may well be that we've veered off that description too much by now and it's no longer a good guideline for me to check?

But anyway, I unfortunately still see both work-items - my own and my colleague's - in my workflow-inbox, so even if the code is technically working it's still not cooperating with what I'd like to do. 😞

pokrakam
Active Contributor
0 Kudos

Almost almost there. USER should be an object.

BaerbelWinkler
Active Contributor
0 Kudos

Sorry, I don't understand (and I partially blame the heat-wave we currently have!).

How do I make it end up as an object when I trigger things via the FM and just fill lt_container_simple via the code you provided?

The container element is still defined as Object Type CL ABAP CLASS ZCL_USER in SWDD so at the moment I don't know where I need to do what (and I'm really sorry about that ....)

But ....

I also just now noticed that there's another optional table I can feed into the FM, tentalicingly called AGENTS with structure SWRAGENT and fields OTYPE and OBJID. So, I just gave that a try and filled it with this:

DATA: ls_agents           TYPE swragent,
      lt_agents           TYPE TABLE OF swragent.

 ls_agents-otype = 'US'.
ls_agents-objid = i_agent.
APPEND ls_agents TO lt_agents.

When I then run my program I only see my own work item and my colleague sees his in our respective workflow inboxes. And a colleague I didn't trigger the item for doesn't see it in his inbox. Which is finally and at long last what I want.(and what I most likely should have figured out how to do via the FM much earlier!) 🙂

Thanks for following me all the way down this rabbit hole - I really appreciate your patience and time!

Answers (0)