Skip to Content
Author's profile photo Christopher Solomon

HCM Processes & Forms: when all else fails…Advanced Generic Services

One thing I have learned over the years with HCM P&F is:

“When all else fails, use an Advanced Generic Service!”

…Sounds great, eh? However, one of the biggest hurdles was first learning how in the heck to even create a working Advanced Generic Service. It seemed like it was some great guarded secret by SAP. To make matters worse, in the “early days” of HCM P&F, there were no examples to even go by…just some very confusing documentation (that still exists today! haha). Thankfully, through the help of people like SAP’s own Michael Bonrat (who headed up HCM P&F in its infancy) and fellow consultant, Derrick Banks, I was able to figure out Advanced Generic Services (AGS) and begin to use them to solve more complicated issues (for example, working around some of the early bugs in the HCM P&F framework and/or handling infotypes it could not handle correctly).

     That was long ago for me. I thought that over the years SAP would roll out more examples and information about Advanced Generic Services and the “pain” I went through would not be suffered by others who came after myself and the early HCM P&F crowd. Sadly, I was mistaken. How to implement and use AGS is still something that comes up all the time. It still is not covered well by SAP in my opinion. Therefore, I am hoping this blog will help to end that for a LOT of people….so here we go….

     First off, you should already know how and where to use regular “generic services”. They are typically used for more of our  “interactive-ness” of HCM P&F. We use them to manipulate form field values such as adding one day to a date, reading values from custom tables, handle user events in configuration, take care of any special business logic for form field validations for data coming back from the user, or anything else that can be repeated without much real harm. 

      However, when it comes to making actual backend updates (create, update, or delete) , this is where you will want (and need) to use Advanced Generic Services (AGS)…and then only when the SAP supplied services (SAP_PA, SAP_PT, or SAP_PD) do not meet your needs.

      One more major thing to know before doing updates with your AGS….from SAP:



  The SAP LUW is controlled solely by the framework. Therefore, it is not permitted to execute COMMIT WORK or ROLLBACK WORK in the FLUSH method or any other method. Similarly, you are not allowed to call external coding (function modules, classes, programs, and so on) that could possibly execute COMMIT WORK or ROLLBACK WORK.

     So how do we use them correctly for updates? It really is as simple as a three step process: 


  1. set an instanced “ID” attribute value in the “do operations” method of your service
  2. use the instanced “ID” value to retrieve your form scenario’s step/stage’s form container values (ie. get all the current form fields and their values) in the “flush” method of your service.
  3. code for you updates based on your form field values


Let us look at this in a bit more detail. I will show you two methods for doing this.

  1. using the “step GUID” (the “newer” way)
  2. using the “process reference number” (which is
    the older way we had to do this before the framework set our “step
    GUID” for us)


There are a handful of fields that the HCM P&F framework will handle for you if you define them in your form scenario fields. One of these is STEP_OBJECT_ID. You can define it in your form fields like:


…defined as type ASR_GUID


We can look at the ONE example from SAP of a AGS on how this is used. Look at the AGS implementation, HRMSSRCF_REQUISITION_ADVANCED. If we look at the class, CL_IM_HRRCF_REQUI_REQUEST, we can see how they defined “STEP_GUID”.


This field is mapped in the process HR_MSSRCF_REQUISITION to the backend service (the AGS) as:


When the “do operations” of this service are triggered (during “check and send” for example), within the “do operations” method, we simply set the STEP_GUID attribute value:


Then in FLUSH…. IF_HRASR00GEN_SERVICE_ADVANCED~FLUSH…we simply use this “step guid” to go re-locate our form’s data container. The SAP AGS does not show a good example of this (though it does show a good one for getting attachment data!). Therefore, here is an example of something we could do to get our form fields:



We can also do something similar simply using our “process reference number”. In our form scenario fields, this is always handled for us (ie. filled in) by the HCM P&F framework engine. We simply define this in our form fields:


…defined as type ASR_REFERENCE_NUMBER


In our Advanced Generic Service, much like using the “step guid”, we can define an attribute of our AGS class as:


Then in our “Do Operations”, we simply “set” this attribute’s value just like we did for “step GUID”. For example,

*** Get the New Reference Number:
*** We execute this logic so once the new reference number is set, we can pass it to the Advanced Service method FLUSH.
LOOP AT service_datasets INTO sd_wa WHERE fieldname = ‘REFERENCE_NUMBER’.
    w_ref_num = sd_wa-fieldvalue.

  IF NOT w_ref_num IS INITIAL.
    me->att_reference_number = w_ref_num.

Then in our FLUSH method, we would simply use this process number to locate our container to retrieve all of our current form fields. You can make this a bit easier by creating a class to read your container based on the reference number/GUID passed.


For example, we could use this like:

   wa_ref_num = me->att_reference_number.  “<– For Advanced Service

  ** Get the data container values for the Reference Number
CALL METHOD zcl_hr_process_and_forms_01=>get_form_container_data
      in_refid           = wa_ref_num
      message_handler    = message_handler
      tbl_data_container = form_data_containers.

Then it is as easy as something like…


  DATA wa_begda TYPE datum.
CLEAR field_value.
READ TABLE form_data_containers WITH KEY fieldname = ‘EFFECTIVE_DATE’
INTO form_data_container.
READ TABLE form_data_container-fieldvalues
INTO field_value INDEX 1.
  wa_begda = field_value.

…to read actual form field values.

Our class code for getting the container correctly would look something like:



From here, you can handle your updates however you like. For example, you could handle PA infotype updates that SAP_PA might not handle well/correctly or include additional business logic that you need.

     In the “old” days of HCM P&F for PA related updates using the decoupled infotype framework, we had to duplicate a lot of code to make it happen like SAP_PA would do it. We had to handle the references to the master data “factory”, handle our containers, handle getting our structure references, and much more. For example, say we wanted to just update infotype 0035, it would look something like:


     Thankfully, we now have the nice little function, HR_CONTROL_INFTY_OPERATION, which handles everything for us and knows whether to use the newer decoupled way or the older “infotype operations” way of doing things. Funny enough, the comments in the function call this the “new infotype framework (NITF) ” and the “old infotype framework (OITF)”. haha

     Similarly, you could handle updates to your own custom “Z” tables or really anything else you might need here in the FLUSH method.

So that is about it. Nothing really magical or secretive to it at all. Hope this helps! As always….till next time….enjoy!

Assigned Tags

      You must be Logged on to comment or reply to a post.
      Author's profile photo Paul Snyman
      Paul Snyman

      Hey Chris,

      Nice post. That HR_CONTROL_INFTY_OPERATION is a great tool for PA Infotypes but I don't think it handles OM (PD) Infotypes, any pearls of wisdom you might offer? I'm hoping there is an equivalent one I'm not aware of.



      Author's profile photo Ioan Radulescu
      Ioan Radulescu

      Hi Chris,

      This blog is exactly what I need right now - if it works 馃檪 . So thanks already, at least I know it's possible.

      I've been trying to set the STEP_OBJECT_GUID in the fields of my scenario but it never gets filled in the SERVICE_DATASETS table - it's there but empty. I tried different UI attributes to no avail.

      Is this still working, or should I revert to using the process number (I just hope that one is also filled out)?

      Would it not be possible to get the current step somehow while my service class is being called?

      And also: is there an official help page to reinforce this "dark art" setting of the field STEP_OBJECT_GUID?


      Author's profile photo Philip Johnston
      Philip Johnston

      thanks for sharing Chris..

      Author's profile photo Ioan Radulescu
      Ioan Radulescu

      Hi everybody,

      My configuration was without a workflow (NO_WORKFLOW). At the point of sending the form there is nothing saved in the T5ASR* tables yet. Maybe that's why there is no STEP_OBJECT_GUID value. I then added PROCESS_REFERENCE_NUMBER (also in the service fields - otherwise it would not appear in the table SERVICE_DATASETS) and I finally got a value there. Unfortunately this number means little if nothing is saved yet on the DB.

      So I looked a little around in DEBUG. SAP is actually calling the service class in CL_HRASR00_DISPATCHER method FLUSH. There we would already have the fields from the records - in the variable "flushes".

      You can see how the service class is called here (in the same FLUSH method):

          CALL METHOD ref_to_act_mapper->flush
               no_commit       = c_no_commit
               message_handler = message_handler
               log_receipts    = act_log_receipts
               is_ok           = is_ok.

      I wonder why SAP didn't pass the content of <FINAL_FLUSHES_WA>-SRV_VALUES_OF_FIELDS in the FLUSH method of the interface IF_HRASR00GEN_SERVICE_ADVANCED. Very annoying.

      I have decided to enhance the mentioned class and transfer the "flushes" variable content. Also I will try to send a message to SAP to pass the content of the "flushes" variable through the interface to the service class.

      @Chris: I'm still very curious about where in the documentation is mentioned this behaviour of the framework towards the passing of the process_number and the step GUID. There are a couple of notes containing some references to these special form fields but nothing really clear (so far). Or where did you hear that from?

      Author's profile photo Alfonso Vandell贸s
      Alfonso Vandell贸s

      Hi Ioan,

      I have the same problem, I need to get some field values for further operation in FLUSH method of my advanced generic service but I don't know how to enhance CL_HRASR00_DISPATCHER class in order to pass flushes to my generic service.

      Did you get it?


      Author's profile photo Christopher Solomon
      Christopher Solomon
      Blog Post Author

      What do you mean you need "further field values"? You have access to ALL fields/values in your Advanced Generic Service. In your FLUSH method, you can use your "reference number" that you set to then get a "handle" back to YOUR data. You can make your own class with a method to read/get the data container (there are plenty of examples of how to do this and the screenshot in my blog SHOWS the code to use) and call something like...

      ** Get the data container values for the Reference Number
      CALL METHOD zcl_hr_process_and_forms=>get_form_container_data
            in_refid           = wa_ref_num
            message_handler    = message_handler

            tbl_data_container = form_data_containers.


          ...and then say we want the "PERNR" field....we can do something like....

          DATA wa_pernr TYPE pernr_d.
      CLEAR field_value.
      READ TABLE form_data_containers WITH KEY fieldname = 'PERNR'
      INTO form_data_container.

        READ TABLE form_data_container-fieldvalues
      INTO field_value INDEX 1.
        wa_pernr = field_value.

      Author's profile photo Alfonso Vandell贸s
      Alfonso Vandell贸s


      Yes, what you are explaining is perfect if there is a workflow template assigned to the process but my case is a process with NO_WORKFLOW, so nothing is saved into database tables to retrieve within flush methods.

      What I've done is to save my needed fields while DO_OPERATIONS in instance attributes of my service and when flush method get them and do some z tables updates.


      Author's profile photo Ioan Radulescu
      Ioan Radulescu

      Hi Alfonso & Chris,

      Indeed. I tried to do the same as you recommended Chris, but as long as no_workflow - nothing is saved in the only step to the completion of the process.

      Alfonso, implement a pre-enhancement to the flush method of CL_HRASR00_DISPATCHER and export the necessary variable to a class attribute of a customer class. For extra safety you could also export the process number to the static attribute of the customer class. Later, when you're calling the service class, call a static method from your customer class (which is de facto a buffer) to retrieve the variable(s) in question - in this method you can check if the process number is the same (for extra safety).

      Author's profile photo Alfonso Vandell贸s
      Alfonso Vandell贸s

      Thanks a lot Iolan,

      In fact I have done similar stuff but saving only relevant fields that I want to transfer in class attributes of the class of advanced generic service, I need only 3 fields. In that way, once FLUSH method is executed I have these fields ready to deal with them.

      Anyway your solution to retrieve ALL data of the container of the form is great!


      Author's profile photo Christopher Solomon
      Christopher Solomon
      Blog Post Author

      Did you not read the blog or look at the screenshots? That is EXACTLY what it shows....having a custom class that pulls ALL the fields of the data container that you can call in your FLUSH method. did you miss that?

      Author's profile photo Alfonso Vandell贸s
      Alfonso Vandell贸s

      Of course I've read it, a lot of times in fact!

      Did you read that we don't have any information saved on database tables T5ASR* to get fields values when NO_WORKFLOW is set on the process?

      I quote a previous response of mine:

      "what you are explaining is perfect if there is a workflow template assigned to the process but my case is a process with NO_WORKFLOW, so nothing is saved into database tables to retrieve within flush methods."

      Have you tried to use your code with NO_WORKFLOW processes?


      Author's profile photo Christopher Solomon
      Christopher Solomon
      Blog Post Author

      Sorry....I guess I should be more clear....where you said "Anyway your solution to retrieve ALL data of the container of the form is great!"....that is what I was referring too.

      In reference to "NO_WORKFLOW"....yes, used it quite a bit (even back before most anyone knew about it, you can find it in my old "tips and tricks" blog). That fact that you are using "NO_WORKFLOW" should not change the behavior of the "flush". Using "NO_WORKFLOW" basically is like having the "SEND_VARIATION" auto set to "save". If you are not seeing your updates/changes, then I suspect you might have an issue in your FLUSH method that is not being caught in error handling therefore you don't see an error nor your expected result (saved/changed data). Are any other changes being made (like via SAP_PA)?

      Author's profile photo Jonathan Bourne
      Jonathan Bourne

      Hi Chris,

      Error Handling from the FLUSH method

      Great blog - thank you for taking the time to write it. Do you have any experience of the error handling in the FLUSH method? I have added an error to the MESSAGE_HANDLER and I was hoping that the message would propogate back to the calling workflow task TS17900108 in the ERROR_MESSAGES binding table. I could then forward the errors to a team to handle the manual PA30/PA40.

      However, as a result of the message handler containing a message type 'E' (Error) the framework is trying to set the work item to status ERROR using function module SAP_WAPI_SET_ERROR (this is called from method HANDLE_ERROR of class CL_HRASR00_PROCESS_ERROR). The function call fails due to the work item being enqueued and then a short dump occurs as a result of an ASSERT command. I'd be interested to know if you've been able to get error reporting from the FLUSH method to work.

      Thanks again,


      Author's profile photo Frank Haschick
      Frank Haschick

      Thanks Chris,

      works like a charm!

      Just two remarks for the "STEP_GUID" part, so others don't have to search:


      you need to assign the step_guid to the step_guid-field, not the scenario_guid field, so like this:

      CALL METHOD cl_hrasr00_process_runtime=>get_instance
      *       scenario_guid         =
               step_guid             = me->step_guid



      The object type for the data_container is CL_HRASR00_DATA_CONTAINER (didn't know that, although its somehow obvious)

      Apart from that, it works good! Just wondering why the FLUSH method is called even after i pressed send (I thought it should be called only after my approval step). Mysteries of the ASR ...

      Best regards,


      Author's profile photo Christopher Solomon
      Christopher Solomon
      Blog Post Author

      Great to hear and thanks for clearing it up (more) for folks!

      As for your issue of when Flush happens....I would have to see your config and where you set up the "save" to happen (ie. in the process start config as "write to application database" for "send variant or in the binding of workflow). Think of it like this though in general...the standard service (SAP_PA or SAP_PD....or those rare few who actually use SAP_PT) will begin the "transaction" (ie lock)....then AGS "flush" methods get called.....then the standard services do their updates/flush.....then they "unlock" (commit transaction)....of course, assuming no errors that cause rollbacks along the way.

      Author's profile photo Frank Haschick
      Frank Haschick

      Chris, you are a genius. Of course in the process start config the setting was set to "write to application database". Boy I debugged nearly half a day for that.

      Thank you very much! 馃檪

      Author's profile photo Christopher Solomon
      Christopher Solomon
      Blog Post Author

      I don't know if I would say "genius".....let's just say I may or may not have known someone who long ago spent quite a lot of time trying to figure out "what the hell is going on?!?!?" when updates were happening on form submit and at the end of workflow once workflow was added to the process (NO_WORKFLOW set before) only to notice that SEND VARIANT was set to "write immediate" after hours of staring at the debugger and stepping through code. 馃槈

      Author's profile photo Former Member
      Former Member

      Great blog very useful

      Author's profile photo ISLAM RASHAD

      Dear Christopher,

      I have a form that is required to maintain educational data (infotype 0022), Family members (Infotype 21), and previous work experience (Infotype 0023).

      Since those infotype has a time constraint 3 we have followed the instructions in SAP note 2202450 and 2214440聽- P&F - ASSERTION_FAILED running a process.

      The initiator submits the form without any problems but the approver receives the run time error Assertion failed in class CL_IM_HRASR00ISR, method GET_SPECIAL_DATA_VALUE.

      Please advice


      Author's profile photo Marcos Oliveira Silva
      Marcos Oliveira Silva

      Hi, all.


      Although the post is a bit old, I'll share a bit of my experience with advanced generic services as it can still help others. We have here SAP ECC 7.40 EHP7.


      First I tried to use STEP_OBJECT_GUID but there was no way system populated that field. And I may have done something wrong that but, surprisingly, I did get PROCESS_REFERENCE_NUMBER. Nonetheless, I struggled a bit thinking it would be populated right way but it was only on the second call of DO_OPERATIONS after pressing the Send button.


      Although I succeeded in getting the process reference number, I could find an entry in the t5asrprocesses table only after the FLUSH method had already been executed and it was too late. As I didn鈥檛 need lots of data to do what I had to, I decided to enhance the generic service implementation class with new attributes and set them in the INITIALIZE and DO_OPERATIONS methods.


      Another big pain was to figure out how to update the database with running into errors. @Chris, the blog helps but the code pasted as pictures and 聽missing some variable declarations is very annoying. Here I am the one changing the process and programming as well. After lots of attempts, I could find in another blog the code that help me and that now I share with you guys. Have fun!


        METHOD if_hrasr00gen_service_advanced~flush.
          DATA: lr_message_list        TYPE REF TO cl_hrpa_message_list,
                lr_masterdata_bl       TYPE REF TO if_hrpa_masterdata_bl,
                container              TYPE REF TO if_hrpa_infty_container,
                old_container          TYPE REF TO cl_hrpa_infotype_container,
                new_container          TYPE REF TO if_hrpa_infty_container,
                new_infotype_container TYPE REF TO cl_hrpa_infotype_container,
                infotype_ref           TYPE REF TO data,
                lv_is_ok               TYPE boole_d,
                lv_dummy               TYPE string,
                ls_msg                 TYPE symsg,
                container_tab          TYPE hrpad_infty_container_tab,
                container_if           TYPE hrpad_infty_container_ref,
                t777d                  TYPE t777d,
                lv_has_error           TYPE boole_d,
                lv_count               TYPE i.
          FIELD-SYMBOLS: <pshdr>  TYPE pshdr,
                         <pnnnn>  TYPE any,
                         <pskey>  TYPE pskey,
                         <record> TYPE any,
                         <pxxxx>  TYPE any.
      * It's up to the process itself to create records for RRSP. This method will only delimit.
          IF me->new_bplan EQ 'PEN2'.
          CREATE OBJECT lr_message_list.
          CALL METHOD cl_hrpa_masterdata_factory=>get_business_logic
              business_logic = lr_masterdata_bl.
          CHECK lr_masterdata_bl IS BOUND.
      * Look for a record valid on the begin date of the existing RRSP record.
          CALL METHOD lr_masterdata_bl->read
              tclas           = 'A'
              pernr           = me->pernr
              infty           = '0169'
              subty           = 'RRSP'
      *       objps           = iv_objps
      *       sprps           = iv_sprps
              begda           = me->begda_rrsp
              endda           = me->begda_rrsp
      *       seqnr           = iv_seqnr
              mode            = if_hrpa_masterdata_bl=>first_intersecting_record
              no_auth_check   = abap_true
              message_handler = lr_message_list
              container_tab   = container_tab
              is_ok           = lv_is_ok.
          READ TABLE container_tab INTO container INDEX 1.
          old_container ?= container.
              CALL METHOD old_container->if_hrpa_infty_container_data~primary_record_ref
                  pnnnn_ref = infotype_ref.
            CATCH cx_hrpa_violated_assertion .
          ASSIGN infotype_ref->* TO <pxxxx>.
          t777d = cl_hr_t777d=>read( infty = '0169' ).
          CREATE DATA infotype_ref TYPE (t777d-ppnnn).
          ASSIGN infotype_ref->* TO <pnnnn> CASTING LIKE <pxxxx>.
          <pnnnn> = <pxxxx>.
          ASSIGN <pnnnn> TO <pshdr> CASTING.
          <pshdr>-infty = '0169'.
          ASSIGN infotype_ref->* TO <record> CASTING LIKE <pxxxx>.
          ASSIGN COMPONENT 'PSKEY' OF STRUCTURE <record> TO <pskey>.
          CALL METHOD lr_masterdata_bl->get_infty_container
              tclas           = 'A'
              pskey           = <pskey>
              no_auth_check   = abap_true
              message_handler = lr_message_list
              container       = container_if.
          new_infotype_container ?= container_if.
      * Adjust the end date, which will ultimately delimit the record
          <pskey>-endda = ( me->effective_date - 1 ).
          new_infotype_container ?= new_infotype_container->modify_key( <pskey> ).
          new_infotype_container ?= new_infotype_container->modify_primary_record( <record> ).
          new_container ?= new_infotype_container.
      * Call the method to make the changes persistent
          CALL METHOD lr_masterdata_bl->modify
              old_container   = old_container
              message_handler = lr_message_list
              is_ok           = lv_is_ok
              container       = new_container.
      * Initialize the buffer in case of errors
          IF lr_message_list->has_error( ) EQ abap_true OR
             lr_message_list->has_abend( ) EQ abap_true.
            lv_has_error = abap_true.
            lv_has_error = abap_false.
          IF lv_has_error = abap_true.
            CALL METHOD lr_masterdata_bl->if_hrpa_buffer_control~initialize.


      Thanks, Marcos.



      Author's profile photo Christopher Solomon
      Christopher Solomon
      Blog Post Author

      That blog was from 2012! haha A LOT has changed since then. Better ways now to do a lot of it (and especially the function I mentioned at the end of the blog). But thank you for following along and keeping this alive with your own experience. I hope it can help others!

      Author's profile photo Anastasiya Korotkova
      Anastasiya Korotkova

      Hello, Christopher!


      Could you please answer, how better to get name of processor and date of processing for every step (several approving steps for form)?

      Is it possible without ABAP-development? I have tried to get this values from container (WF), but it can not be inputed from contaner to the fiori form. Nothing happens. I checked the container in SWIA and all of the values is displayed there, but nothing in form.


      Thank you a lot!

      Anastasia K.