Skip to Content

Welcome back to this bad romance series of Approve Leave Request from ESS/MSS and Fiori UX. In Part 1 and Part 2. We have established a case and a workflow to run synchronously from both sides.

In this part I will explain you the technical part of the workflow we have just discussed. So get set go!

Technical Part of Leave Request Multi Level Workflow

To cater the customized changes in the workflow, I have created a customized class with few methods to get my logic working.

This is my customized class with a few methods, lets go hold of these quickly.

1. Activity: Set initial approver (I have a method for that) Importing: Request ID, Exporting: Agent (initial approver). Same import export binding is done from method to task and from task to Workflow. Code is as below:

METHOD set_initial_approver.

    DATA: lr_request        TYPE REF TO cl_pt_req_request,
          ls_owner          TYPE ptreq_actor_struc_flat,
          ls_owner_uname    TYPE sysid,
          lv_oid            TYPE os_guid,
          lt_approvers_temp TYPE ptreq_approver_tab,
          ls_approvers_temp TYPE ptreq_approver_struc,
          lv_item_list_id   TYPE ptreq_header-item_list_id,
          lv_item_ins       TYPE ptreq_items-item_ins,
          lv_infotype       TYPE infty,
          lv_pernr          TYPE pernr_d,
          lv_awart          TYPE pa2001-awart,
          lv_uname          TYPE syst_uname,
          lv_pernr_nxtappr  TYPE objektid,
          ls_attribs        TYPE ptreq_attribs_struc_flat.

* Move request to Globally Unique Identifier
    lv_oid = iv_request_id.

* Enqueue the request
    CALL FUNCTION 'ENQUEUE_EPTREQ'
      EXPORTING
        mode_ptreq_header = 'S'
        mandt             = sy-mandt
        request_id        = iv_request_id
      EXCEPTIONS
        foreign_lock      = 1
        system_failure    = 2
        OTHERS            = 3.

* Get the request object instance
    lr_request ?= ca_pt_req_header=>agent->if_os_ca_persistency~get_persistent_by_oid( lv_oid ).

* Get Owner Details
    ls_owner = lr_request->if_pt_req_request~get_owner( ).

    lv_pernr       = ls_owner-pernr.
    ls_owner_uname = ls_owner-user.

    CONDENSE ls_owner_uname NO-GAPS.

    MOVE ls_owner_uname TO lv_uname.

* Get Att./abs. type
    SELECT SINGLE item_list_id
      FROM ptreq_header
      INTO lv_item_list_id
      WHERE request_id = iv_request_id.

    IF sy-subrc = 0.

      SELECT SINGLE item_ins
        FROM ptreq_items
        INTO lv_item_ins
        WHERE item_list_id = lv_item_list_id.

      IF sy-subrc = 0.

        SELECT SINGLE infotype subty
          FROM ptreq_attabsdata
          INTO (lv_infotype, lv_awart)
          WHERE item_id = lv_item_ins
            AND pernr   = lv_pernr.

      ENDIF.

    ENDIF.

* Get all leave request approvers
    CALL FUNCTION 'ZWF_GET_ALL_APPROVERS_LEAVE'
      EXPORTING
        pernr        = lv_pernr
        awart        = lv_awart
        initiator    = lv_uname
      IMPORTING
        et_approvers = lt_approvers_temp.

    READ TABLE lt_approvers_temp INTO ls_approvers_temp
      INDEX 1.
    IF sy-subrc = 0.

      lv_pernr_nxtappr  = ls_approvers_temp-pernr.

      CONCATENATE 'US' ls_approvers_temp-SYS_USER
        INTO ev_nxtappr.

      CONDENSE ev_nxtappr NO-GAPS.

* Set Next Approver
      CALL METHOD lr_request->if_pt_req_request~set_next_processor
        EXPORTING
          im_actor_type = 'P'
          im_plvar      = '01'
          im_otype      = 'P'
          im_objid      = lv_pernr_nxtappr. " PERNR of Next Approver

      IF sy-subrc = 0.
* Provides all the attributes of the request
CALL METHOD lr_request->if_pt_req_request~workarea_version->get_all_attribs
          IMPORTING
            ex_all_attribs = ls_attribs.

* Create Persistent Database Version
        CALL METHOD lr_request->clone_to_old.

* Commit work and wait
        COMMIT WORK AND WAIT.

      ENDIF.

    ENDIF.

* Dequeue the request
    CALL FUNCTION 'DEQUEUE_EPTREQ'
      EXPORTING
        mode_ptreq_header = 'S'
        request_id        = iv_request_id.

  ENDMETHOD.

Code is self-explanatory, ask me in comments if you don’t get anything. I am NOT going to explain FM: ZWF_GET_ALL_APPROVERS_LEAVE because I have my own logic to fetch next approver. You may use your own.

2. Activity: Getting Employee Name is easy. Go, get it from PA0002, pass PERNR.

3. Activity: Approval Process is a standard task TS12300097 SAP use to approve leave request.

4. Condition: Check Approve: Request Status is REJECTED, WITHDRAWN or APPROVED

5. In case of APPROVED: Activity: Get Next Approver (Method: SET_NEXT_APPROVER in my class). Code is well documented, please go through. Import: Request ID, Current Agent) Exporting: Next Approver. Same binding is done from Method to Task and from Task to WF.

METHOD set_next_approver.


    DATA: lr_request        TYPE REF TO cl_pt_req_request,
          ls_owner          TYPE ptreq_actor_struc_flat,
          ls_owner_uname    TYPE sysid,
          lv_oid            TYPE os_guid,
          lt_approvers_temp TYPE ptreq_approver_tab,
          ls_approvers_temp TYPE ptreq_approver_struc,
          lv_item_list_id   TYPE ptreq_header-item_list_id,
          lv_item_ins       TYPE ptreq_items-item_ins,
          lv_infotype       TYPE infty,
          lv_n_agent        TYPE swp_agent,
          lv_index          TYPE sy-index,
          lv_pernr          TYPE pernr_d,
          lv_awart          TYPE pa2001-awart,
          lv_uname          TYPE syst_uname,
          lv_pernr_nxtappr  TYPE objektid,
          lv_req_notice     TYPE tim_req_notice,
          ls_attribs        TYPE ptreq_attribs_struc_flat.

** wait for 60 Seconds for completion of all DB commits
    WAIT UP TO 60 SECONDS.

* Move request to Globally Unique Identifier
    lv_oid = iv_request_id.

* Enqueue the request
    CALL FUNCTION 'ENQUEUE_EPTREQ'
      EXPORTING
        mode_ptreq_header = 'S'
        mandt             = sy-mandt
        request_id        = iv_request_id
      EXCEPTIONS
        foreign_lock      = 1
        system_failure    = 2
        OTHERS            = 3.

* Get the request object instance
    lr_request ?= ca_pt_req_header=>agent->if_os_ca_persistency~get_persistent_by_oid( lv_oid ).

* Get Owner Details
    ls_owner = lr_request->if_pt_req_request~get_owner( ).

    lv_pernr       = ls_owner-pernr.
    ls_owner_uname = ls_owner-user.

    MOVE iv_n_agent TO lv_n_agent.

    REPLACE FIRST  OCCURRENCE OF 'US' IN lv_n_agent
      WITH space.

    CONDENSE: ls_owner_uname NO-GAPS,
              lv_n_agent     NO-GAPS.

    MOVE ls_owner_uname TO lv_uname.

* Get Att./abs. type
    SELECT SINGLE item_list_id
      FROM ptreq_header
      INTO lv_item_list_id
      WHERE request_id = iv_request_id.

    IF sy-subrc = 0.

      SELECT SINGLE item_ins
        FROM ptreq_items
        INTO lv_item_ins
        WHERE item_list_id = lv_item_list_id.

      IF sy-subrc = 0.

        SELECT SINGLE infotype subty
          FROM ptreq_attabsdata
          INTO (lv_infotype, lv_awart)
          WHERE item_id = lv_item_ins
            AND pernr   = lv_pernr.

      ENDIF.

    ENDIF.

* Get all leave request approvers
    CALL FUNCTION 'ZWF_GET_ALL_APPROVERS_LEAVE'
      EXPORTING
        pernr        = lv_pernr
        awart        = lv_awart
        initiator    = lv_uname
      IMPORTING
        et_approvers = lt_approvers_temp.

    READ TABLE lt_approvers_temp TRANSPORTING NO FIELDS
      WITH KEY sys_user = lv_n_agent.

    lv_index = sy-tabix.
    ADD 1 TO lv_index.

    READ TABLE lt_approvers_temp INTO ls_approvers_temp
      INDEX lv_index.
    IF sy-subrc = 0.

      CONCATENATE 'Request forwarded to' ls_approvers_temp-name
        INTO lv_req_notice
          SEPARATED BY space.

      lv_pernr_nxtappr  = ls_approvers_temp-pernr.

      CONCATENATE 'US' ls_approvers_temp-sys_user
        INTO ev_nxtappr.

      CONDENSE ev_nxtappr NO-GAPS.

* Set Next Approver
      CALL METHOD lr_request->if_pt_req_request~set_next_processor
        EXPORTING
          im_actor_type = 'P'
          im_plvar      = '01'
          im_otype      = 'P'
          im_objid      = lv_pernr_nxtappr. " PERNR of Next Approver

      IF sy-subrc = 0.

* Provides all the attributes of the request
        CALL METHOD lr_request->if_pt_req_request~workarea_version->get_all_attribs
          IMPORTING
            ex_all_attribs = ls_attribs.

* Create Persistent Database Version
        CALL METHOD lr_request->clone_to_new.

* Commit work and wait
        COMMIT WORK AND WAIT.

      ENDIF.

    ENDIF.

* Dequeue the request
    CALL FUNCTION 'DEQUEUE_EPTREQ'
      EXPORTING
        mode_ptreq_header = 'S'
        request_id        = iv_request_id.

  ENDMETHOD.

6. Activity: Set Status Send. Importing: Request ID, Exporting: New Status

7. Activity: Set Status Approved. Importing: Request ID, Exporting: New Status

8. Activity: Get Current Status. Latest status of the request from PTREQ_HEADER

9. Loop Condition

Rest is standard. Email and SMS logic is all over internet. Find and send them.

Now we are done with the Workflow development and explanation. Technical part is done as well, if you have any question, please feel free to comment.

Leave Request Approved but dump in ST22

Since, we are done with the workflow. Pretty easy yeah? But where is the challenge.

Go to Step number 6: Change Status to SEND. Somehow SAP don’t change this status and throws and dump. This dump comes whenever the Approve button is pressed either from Fiori or ESS/MSS. Why this dump occurs? Let’s catch that! I have analyzed this thing and I have concrete answer to this (this is the pain area for most of us).

Dump details are as follows:

Runtime Errors: UNCAUGHT_EXCEPTION

Exception: CX_OS_DB_INSERT

ABAP Program: CA_PT_REQ_HEADER==============CP

Just putting in a screenshot just to be sure.


Above dump says that there an expectation when inserting in some table? Which table? Let’s analyze.

Dump occurred in this method: MAP_SAVE_TO_DATABASE line: 152. This dump occurred when I tried to change the Status from APPROVE to SENT in my custom (CHANGE_STATUS_SENT) method.

Scenario for CX_OS_DB_INSERT Dump

  1. Status didn’t get changed
  2. There was no next approver
  3. Loop found the current status of request as SENT, loop condition met.
  4. Leave Request again sent to the same approver again!

How irritating for the user who has just approved the leave request and came back to him! Wow! Great! Amazing! Imagine this happening with 1800 users!

Why Dump Occurred

Why this dump came? Because by default sometimes I don’t know how. SAP put an entry of status SENT when leave is approved (specially in case of multi-level). That SENT row is already there in table PTREQ_HEADER and I am trying to insert the same row. So, it says I have that row already which you are trying to insert and dumps! Let me show you the table.


You see above? All rows are set to SENT. Why? Because of that nasty dump!

Solution to dump: Runtime Errors: UNCAUGHT_EXCEPTION. Exception: CX_OS_DB_INSERT. ABAP Program: CA_PT_REQ_HEADER==============CP

You could have noticed that I have put a WAIT statement in my methods, yeah because let SAP do its entries in the said table then I will do my trick. So, let SAP put a SENT status in the table after the request is approved. Then I am creating a new row with status APPROVED then SENT again.

Leave Request posted but the Document in PTARQ is in Error

This is an easy catch. If any document goes in to error (remember, not talking about workflow).

Just run the following two reports:

  1. RPTARQERR for less than 50 employees
  2. RPTARQERR_ALL for more

That’s all folks! I hope you have enjoyed this series and learned a lot. Please let me know if you have any queries.

Thank you!

To report this post you need to login first.

2 Comments

You must be Logged on to comment or reply to a post.

  1. Christian Köhler

    Hi Mansoor,

    thank you very much for that technical deep dive in that 3 parts.
    What is a little unclear for me is the following: Why didn’t you use the BAdI PT_ABS_REQ with method get_multiple_approvers in combination with the standard workflow WS33700137?

    Thanks in advance and best regards
    Christian

    (2) 
  2. Mansoor Ahmed Post author

    Thanks Christian!

    Actually, the method get multiple approvers works when we are dealing with the standard WF, in this case if you notice we have used the custom one, plus we are fetching apporvers according to client’s needs. Not from Org structure.

    Cheers
    MJ!

    (0) 

Leave a Reply