Homelab, Linux, JS & ABAP (~˘▾˘)~
 

[ABAP] Leave Request Approval Workflow with Escalation

During the leave request approval process the standard workflow WS12300111 does not include any deadlines. To archieve this you can simply copy the workflow and add a deadline at the approval task (Step 38). There are two possible ways to forward the approval step to the next processor:
1. When deadline is reached, forward the current workitem to the next approver.
2. When deadlines is reached, set current workitem to obsolete and create a new workitem and assign it to the next approver. Then you also have to add the new step ID to this BAdI Filter.

I selected the first option. In my demo the escalation is triggered after just three minutes.

The deadline generates a new exit on the approval step.

In this new exit you are able to call your own logic on finding the new approver, forward the workitem and inform him via mail if necessary.

To forward the request to the next approver, you have to forward the workitem and also set this new approver as next processor in the current leave request.
So first identify the next approver. I used RH_GET_LEADER for this.

*--------------------------------------------------*
* Get next approver
*--------------------------------------------------*
    CALL FUNCTION 'RH_GET_LEADER'
      EXPORTING
        plvar                     = '01'
        keydate                   = sy-datum
        otype                     = is_approver-otype
        objid                     = CONV realo( is_approver-objid )
      IMPORTING
        leader_type               = lv_leader_type
        leader_id                 = lv_leader_id
      EXCEPTIONS
        no_leader_found           = 1
        no_leading_position_found = 2
        OTHERS                    = 3.

    CHECK lv_leader_type EQ 'P' AND lv_leader_id IS NOT INITIAL.

    CALL FUNCTION 'HR_GET_USER_FROM_EMPLOYEE'
      EXPORTING
        pernr             = CONV pernr_d( lv_leader_id )
        iv_with_authority = abap_false
      IMPORTING
        user              = lv_userid_approver.

    ev_approver-otype = |US|.
    ev_approver-objid = lv_userid_approver.


Then set this new approver as next processor in your request. While doing this, it’s recommended to enqueue and dequeue the request. To get the current request object use class cl_pt_req_badi.

*--------------------------------------------------*
* Set new approver in request
*--------------------------------------------------*
    " Enqueue the request
    CALL FUNCTION 'ENQUEUE_EPTREQ'
      EXPORTING
        mode_ptreq_header = 'S'
        mandt             = sy-mandt
        request_id        = io_req->req_id
      EXCEPTIONS
        foreign_lock      = 1
        system_failure    = 2
        OTHERS            = 3.

    " Get the request object instance
    CALL METHOD cl_pt_req_badi=>get_request
      EXPORTING
        im_req_id  = io_req->req_id
      IMPORTING
        ex_request = DATA(lcl_request).

    CALL METHOD lcl_request->set_next_processor
      EXPORTING
        im_actor_type = 'P'
        im_plvar      = '01'
        im_otype      = 'P'
        im_objid      = CONV #( lv_leader_id ). " PERNR of Next Approver
    IF sy-subrc = 0.
      COMMIT WORK AND WAIT.
    ENDIF.

    " Dequeue the request
    CALL FUNCTION 'DEQUEUE_EPTREQ'
      EXPORTING
        mode_ptreq_header = 'S'
        request_id        = io_req->req_id.

Finally forward the workitem to the new approver. There of course you need the right workitem ID of the approving step.

*--------------------------------------------------*
* Forward workitem to next approver
*--------------------------------------------------*
    CALL FUNCTION 'SAP_WAPI_FORWARD_WORKITEM'
      EXPORTING
        workitem_id  = iv_wi_id                "woritem id of approving step
        user_id      = lv_userid_approver
        language     = sy-langu
        do_commit    = 'X'
      IMPORTING
        return_code  = lv_subrc.
    IF lv_subrc <> 0.
    ENDIF.

[ABAP] Workflow mail step -> Add attachment

Recently I got the demand to enhance the leave request approvel workflow with some mail steps. These mails should also include the attachments, which can be uploaded in the “Leave Request” Fiori App.

To achive this, I used the exit functionality in the mail step.

There you have to provide a class name. The class has to implement the following interface:

IF_SWF_IFS_WORKITEM_EXIT

You will get a method you can implement.

This Method has two parameters, the “Event Name” and the “Workitem Context”.

With the event, you are able to control, in which situations your exit should be fired. In my case, I just need my code run when the workitem is created (event “CREATED”). You’ll find all usable events in domain SWW_EVTTYP.

To get and pass back the attachment to the mail step, a few things need to be done.
First, get the curent workitem container and check, if there is already any attachment. If not, read the request id from the leave request. With this request id, you are able to find the attachment you need. I’ve already written a method for that, you’ll find below. Then convert the xstring to a solix table, create the attachment document with function module “SO_DOCUMENT_INSERT_API1” and “SWO_CREATE” and finally pass it to the container.

 METHOD if_swf_ifs_workitem_exit~event_raised.

    DATA: lv_container     TYPE REF TO if_swf_ifs_parameter_container,
          lv_attach        TYPE TABLE OF obj_record,
          lv_folder_id     TYPE soodk,
          wa_document_info TYPE sofolenti1,
          lv_data          TYPE sodocchgi1,
          lv_objtype       TYPE swo_objtyp,
          lv_objkey        TYPE swo_typeid,
          lv_return        TYPE swotreturn,
          lv_sofm          TYPE swo_objhnd,
          lv_objject       TYPE obj_record,
          tb_obj           TYPE TABLE OF obj_record,
          it_solix_tab1    TYPE solix_tab,
          req_id           TYPE tim_req_id,
          lv_doc_type      TYPE so_obj_tp.

    CHECK im_event_name = 'CREATED'.

* Fetch Container

    lv_container = im_workitem_context->get_wi_container( ).

* Read attachment to confirm that there is no duplication

    TRY.
        lv_container->get(
          EXPORTING
            name  = '_ATTACH_OBJECTS'
          IMPORTING
            value = lv_attach ).

      CATCH: cx_swf_cnt_elem_not_found,
             cx_swf_cnt_elem_type_conflict,
             cx_swf_cnt_unit_type_conflict,
             cx_swf_cnt_container.
    ENDTRY.

    CHECK lv_attach IS INITIAL.

    CALL FUNCTION 'SO_FOLDER_ROOT_ID_GET'
      EXPORTING
        owner     = sy-uname
        region    = 'B'
      IMPORTING
        folder_id = lv_folder_id.

* Get Request ID

    lv_container->get(
      EXPORTING
        name       = 'RequestId'        
      IMPORTING
        value      = req_id ).          

* Get XSTRING of attachment

    zcl_xxx=>get_req_attachment(
      EXPORTING
        iv_req_id     = req_id                
      IMPORTING
        ev_xstring    = DATA(xstring)
        es_attachment = DATA(ls_attachment) ).

    CHECK xstring IS NOT INITIAL.

* Get document type

    SELECT SINGLE doc_type FROM toadd INTO @lv_doc_type
       WHERE mimetype EQ @ls_attachment-file_type.
    IF sy-subrc NE 0.
      lv_doc_type = 'PDF'. "If doc_type not found, at least try with pdf.
    ENDIF.

* Create and set document

    it_solix_tab1 = cl_document_bcs=>xstring_to_solix( xstring ).

* Creating First attachment

    lv_data-obj_name   = ls_attachment-file_name.
    lv_data-obj_descr  = ls_attachment-file_name.
    lv_data-obj_langu  = sy-langu.
    lv_data-sensitivty = 'P'.
*    lv_data-doc_size   = ls_attachment-file_size.

    CALL FUNCTION 'SO_DOCUMENT_INSERT_API1'
      EXPORTING
        folder_id                  = lv_folder_id
        document_data              = lv_data
        document_type              = lv_doc_type
      IMPORTING
        document_info              = wa_document_info
      TABLES
        contents_hex               = it_solix_tab1
      EXCEPTIONS
        folder_not_exist           = 1
        document_type_not_exist    = 2
        operation_no_authorization = 3
        parameter_error            = 4
        x_error                    = 5
        enqueue_error              = 6
        OTHERS                     = 7.

* Populate object type and object key for create an instance

    lv_objtype = 'SOFM'.
    lv_objkey  = wa_document_info-doc_id.

    CALL FUNCTION 'SWO_CREATE'
      EXPORTING
        objtype           = lv_objtype
        objkey            = lv_objkey
      IMPORTING
        object            = lv_sofm
        return            = lv_return
      EXCEPTIONS
        no_remote_objects = 1
        OTHERS            = 2.

* Prepare for attaching the object to container

    lv_objject-header  = 'OBJH'.
    lv_objject-type    = 'SWO'.
    lv_objject-handle  = lv_sofm.
    APPEND lv_objject TO tb_obj.

*—can be used for other workitems

    lv_container->set(
      EXPORTING
        name  = '_ATTACH_OBJECTS'
        value = tb_obj[] ).

*–this will add the attachment to email

    lv_container->set(
      EXPORTING
        name  = 'ATTACHMENTS'
        value = tb_obj[] ).

* Commit the changes

    im_workitem_context->do_commit_work( ).

  ENDMETHOD.

The following function modules are needed to read the leave request attachment:

  METHOD get_req_attachment.

    DATA: lv_has_errors     TYPE  ptreq_has_error_flag,
          lt_messages       TYPE  ptarq_uia_messages_tab,
          lt_attachments    TYPE  hress_t_ptarq_att_info,
          ex_has_attachment TYPE  boole_d.

    CALL FUNCTION 'PT_ARQ_ATTACHMENT_GET'
      EXPORTING
        im_request_id     = iv_req_id
*       IM_VERSION_NO     =
*       IM_DOCUMENT_TYPE  =
      IMPORTING
        ex_has_errors     = lv_has_errors
        ex_messages       = lt_messages
        et_attachments    = lt_attachments
        ex_has_attachment = ex_has_attachment.

    CHECK ex_has_attachment EQ abap_true.

    TRY.
        es_attachment = lt_attachments[ 1 ].
      CATCH cx_sy_itab_line_not_found.
    ENDTRY.


    CALL FUNCTION 'PT_ARQ_ATTACHMENT_DETAIL_GET'
      EXPORTING
        iv_arc_doc_id   = es_attachment-archiv_doc_id
*        iv_doc_type     =
      IMPORTING
        ex_file_content = ev_xstring
        ex_has_errors   = lv_has_errors
        ex_messages     = lt_messages.

  ENDMETHOD.

[ABAP] Modify Infotype

METHOD operate_it.

    DATA ls_return TYPE bapireturn1.
    DATA key       TYPE bapipakey.

    CALL FUNCTION 'BAPI_EMPLOYEE_ENQUEUE'
      EXPORTING
        number = is_2002-pernr
      IMPORTING
        return = ls_return.
    IF ls_return IS NOT INITIAL.
      RAISE EXCEPTION TYPE zcx_xxx MESSAGE ID ls_return-id TYPE ls_return-type NUMBER ls_return-number.
    ENDIF.

    CALL FUNCTION 'HR_INFOTYPE_OPERATION'
      EXPORTING
        infty         = '2002'
        number        = is_2002-pernr
        subtype       = is_2002-subty
        objectid      = is_2002-objps
        lockindicator = is_2002-sprps
        validityend   = is_2002-endda
        validitybegin = is_2002-begda
        recordnumber  = is_2002-seqnr
        record        = is_2002_chg
        operation     = iv_actio          "MOD,INS,DEL
        tclas         = 'A'
        dialog_mode   = '0'
      IMPORTING
        return        = ls_return
        key           = key.
    IF ls_return IS NOT INITIAL.
      RAISE EXCEPTION TYPE zcx_xxx MESSAGE ID ls_return-id TYPE ls_return-type NUMBER ls_return-number.
    ENDIF.

    CALL FUNCTION 'BAPI_EMPLOYEE_DEQUEUE'
      EXPORTING
        number = is_2002-pernr.

ENDMETHOD.

[ABAP] Read infotype records

* Read multiple infotype records
DATA: ls_p0001 TYPE p0001,
      lt_p0001 TYPE TABLE OF p0001.

* Initialise Infotyp reader 
cl_hrpa_read_infotype=>get_instance( IMPORTING infotype_reader = DATA(lr_infotype_reader) ).

* Read Infotyp 1
lr_infotype_reader->read( EXPORTING tclas         = 'A'
                                    pernr         = 1
                                    infty         = '0001'
                                    begda         = if_hrpa_read_infotype=>low_date
                                    endda         = if_hrpa_read_infotype=>high_date
                                    no_auth_check = abap_true
                          IMPORTING infotype_tab  = DATA(lt_infotype)
                                    data_exists   = DATA(lv_data_exists) ).

LOOP AT lt_infotype INTO DATA(ls_infotype).
  cl_hr_pnnnn_type_cast=>prelp_to_pnnnn( EXPORTING prelp = ls_infotype
                                         IMPORTING pnnnn = ls_p0001 ).
  APPEND ls_p0001 TO lt_p0001.
ENDLOOP.

cl_demo_output=>display( lt_p0001 ).
* Read single infotype record
DATA: ls_p0001 TYPE p0001.

* Initialise Infotyp reader
  cl_hrpa_read_infotype=>get_instance( IMPORTING infotype_reader = DATA(lr_infotype_reader) ).

* Read Infotyp 1
  lr_infotype_reader->read_single( EXPORTING tclas         = 'A'
                                             pernr         = 1
                                             infty         = '0001'
                                             subty         = space
                                             objps         = space
                                             sprps         = if_hrpa_read_infotype=>unlocked
                                             begda         = '20200101'
                                             endda         = '20200131'
                                             mode          = if_hrpa_read_infotype=>first_record_containing_begda
                                             no_auth_check = abap_true
                                   IMPORTING pnnnn         = ls_p0001 ).

cl_demo_output=>display( ls_p0001 ).