Skip to Content
Author's profile photo Mansoor Ahmed

SAP Fiori and SAP ESS/MSS Leave Request Approval – A Bad Bad Romance – Part 3

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!

Assigned Tags

      9 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Christian Köhler
      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

      Author's profile photo Mansoor Ahmed
      Mansoor Ahmed
      Blog 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!

      Author's profile photo Pankaj Jain
      Pankaj Jain

      Hi mansoor,

       

      Thanks for the blog, i had one doubt in you are setting same status i.e c_reqtrans_approveis that right because we need to set it as sent so can you please guide ?

       

      Thanks in advance,

       

      Paresh g

       

       

      Author's profile photo Mansoor Ahmed
      Mansoor Ahmed
      Blog Post Author

      I have mentioned this issue and resolution in this blog series. Please follow all parts.

      Author's profile photo Mathana Kumar V A
      Mathana Kumar V A

      Hi Mansoor,

       

      I have copied the workflow for leave request WS33700137 and customized for multilevel approver.

      The front End is Fiori,

      All the process of multilevel approval and rejections works fine, but in the back end when try to change the 2nd level to third level approval, i get the below dump. how to handle it.

      Exception 'CX_OS_DB_INSERT' was raised, but it was not caught anywhere along the call hierarchy. Since exceptions represent error situations, and this error was not adequately responded to, ABAP program 'CB_PT_REQ_NOTICE==============CP' had to be terminated.

      How to correct the error If the error occurs in a non-modfied SAP program, you might be able to find a solution in the SAP Notes system. If you have access to the SAP Notes system, check there first using the following keywords: "UNCAUGHT_EXCEPTION" CX_OS_DB_INSERT "CB_PT_REQ_NOTICE==============CP" bzw. CB_PT_REQ_NOTICE==============CM00F "MAP_SAVE_TO_DATABASE"

      in brief i have the code as below when i try to change the status from one level approver to next level approver

      IF im_current_level NE im_total_level.
      *

      *
      DO.
      SELECT * FROM
      ptreq_header
      INTO TABLE lt_ptreq_header
      WHERE request_id = lv_request_id.
      IF sy-subrc EQ 0.
      SORT lt_ptreq_header BY version_no DESCENDING.
      READ TABLE lt_ptreq_header INTO ls_ptreq_header INDEX 1.
      IF ls_ptreq_header-status = 'APPROVED'.
      ***-->Enqueue the request
      CALL FUNCTION 'ENQUEUE_EPTREQ'
      EXPORTING
      mode_ptreq_header = 'S'
      mandt = sy-mandt
      request_id = lv_request_id
      EXCEPTIONS
      foreign_lock = 1
      system_failure = 2
      OTHERS = 3.
      IF sy-subrc EQ 0.

      CALL METHOD cl_pt_req_badi=>get_request
      EXPORTING
      im_req_id = lv_request_id
      IMPORTING
      ex_request = request.

      lcl_req_request ?= request.

      CALL METHOD cl_pt_req_badi=>initiate_state_transition
      EXPORTING
      im_request = request
      im_event = event
      IMPORTING
      ex_new_status = lvv_status.
      *
      * CALL METHOD lcl_req_request->clone_to_old.

      COMMIT WORK AND WAIT.
      IF lvv_status IS NOT INITIAL.
      CLEAR : lt_ptreq_header, ls_ptreq_header.
      SELECT * FROM
      ptreq_header
      INTO TABLE lt_ptreq_header
      WHERE request_id = lv_request_id.
      IF sy-subrc EQ 0.
      SORT lt_ptreq_header BY version_no DESCENDING.
      READ TABLE lt_ptreq_header INTO ls_ptreq_header INDEX 1.

      IF sy-subrc EQ 0.
      IF ls_ptreq_header-status = 'SENT'.
      EXIT.
      ELSE.
      ex_error = 'HEADER APPROVED STATUS'.
      WAIT UP TO 5 SECONDS.
      ENDIF.
      ENDIF.
      ELSE.
      ex_error = 'Not Sent status'.
      WAIT UP TO 5 SECONDS.
      ENDIF.
      ELSE.
      ex_error = 'ENQUEUE Failed'.
      WAIT UP TO 5 SECONDS.
      ENDIF.
      **-->if in approved status and it has to go as sent status
      ELSE.
      ex_error = 'NOT AS APPROVED'.
      WAIT UP TO 5 SECONDS.
      ENDIF.
      ENDIF.
      ENDIF.
      ENDDO.

      ***-->Dequeue the request
      CALL FUNCTION 'DEQUEUE_EPTREQ'
      EXPORTING
      mode_ptreq_header = 'S'
      request_id = lv_request_id.

       

      How to over come this dump....even i kept 30 seconds wait also before the sent status.

      Author's profile photo Mansoor Ahmed
      Mansoor Ahmed
      Blog Post Author

      Hi,

      I have mentioned the solution in this blog. Please see the heading

      Scenario for CX_OS_DB_INSERT Dump

       

      Author's profile photo AbdulRafe Ashrafi
      AbdulRafe Ashrafi

      AS Mansoor,

      Yes your blog is very conclusive.

      In our scenario, for 2 level leave request , followed same logic but using BO instead of class.

      Issue is with FIORI when First level request approve/reject leave request item is not disappeared (cleared). After 2 level approver approve/reject the request item get cleared from the list.

      but client requirement is that after 1 level action it should get cleared from that first approver item list.

      it is working fine in ESS Portal.

      Thanks,

      Abdulrafe

      Author's profile photo Mostafa Naghizadeh
      Mostafa Naghizadeh

      Hi,

      I don't get this development, why you used all these methods!!

       

      A Standard Badi for Multi Approval and Enabling the Business Function. ( You can have approval levels from wherever you like!  FM, WebService, Customizing Table or etc )

      for email and SMS, you could use a custom program that runs after or with PTARQ POST.

      For your dump, it is because the LEAVE Request has Persistent Class and all related objects should have similar keys for OS UPDATE and COMMIT. Especially the ACTORS Object

       

      Author's profile photo vishal agrawal
      vishal agrawal

      Dear Mansoor,

      I have a question regarding modifying PTREQ_ATTABSDATA table. In my requirement i modify the table in a custom program. Can you please tell me if there's any BAPI BADI or class to modify PTREQ_ATTABSDATA?

      Regards,

      Vishal Agrawal