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

[ABAP Env] Create Data Model & OData Service

Recently I worked through the tutorial on creating a travel bookings app in the SAP Cloud Platform ABAP Environment.

Find a good introduction and overview on this topic here: Getting Started with ABAP in the Cloud – Part I
And the travel bookings app tutorial here: Getting Started with ABAP in the Cloud – Part II

These are my notes on the steps needed to create the data model and publish it as oData service.

#LayerNomenclatureDescription
1Database TableZTABLEPlace your raw data first
2Data Definition (Interface View)ZI_Relation between different tables (e.g. currency or text table)
3Projection View (Consumption View)ZC_Configure the UI depending on your scenario.
Use different projection views for different usages of the same interface view and the same physical table.
4Service DefinitionZSD_Expose the projection view (and underlying associations like currency, country…) as service
5Service BindingZSB_How to we want to make the service available? Defines the binding type (OData V2 / OData V4)
Activate it with the “Activate” Button within the editor window.
Select the Entity and hit “Preview…” to see whtat we defined in our projection view.

If you’ve done this, you are able to view the data in a generated Fiori Elements app. But if you also want to create, edit, delete data, you’ll have to add some behavior functionality.

6Behavior Definition on Data DefinitionZI_Created on top of the Data Definition. Will get the same name es the Data Definition.
Implementation Type: Managed
Defines the operations create, delete, edit.
7Behavior Implementation on Definition ViewZBP_I_The code for the behavior… For the travel app tutorial, some logic for a generated unique key and field validation.
The class inherits from cl_abap_behavior_handler.
8Behavior Definition on Projection ViewZC_Created on top of the Projection View. Will get the same name es the Projection View.
Defines the operations create, delete, edit.

[ABAP] Select-Option for ALV Layout

Create a ALV Layout Variant
Select-Option for Layout Variant on Selectionscreen
SELECTION-SCREEN BEGIN OF BLOCK b3 WITH FRAME TITLE text-b03.
PARAMETERS: p_vari TYPE slis_vari.
SELECTION-SCREEN END OF BLOCK b3.

INITIALIZATION.

  "Load default layout
  DATA: ls_layout TYPE salv_s_layout_info,
        ls_key    TYPE salv_s_layout_key.

  ls_key-report = sy-repid.

  ls_layout = cl_salv_layout_service=>get_default_layout( s_key    = ls_key
                                                          restrict = '1' ).
  p_vari = ls_layout-layout.

AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_vari.

  "Value Help
  DATA: ls_layout TYPE salv_s_layout_info,
        ls_key    TYPE salv_s_layout_key.

  ls_key-report = sy-repid.

  ls_layout = cl_salv_layout_service=>f4_layouts( s_key    = ls_key
                                                  restrict = '1' ).
  p_vari = ls_layout-layout.

[ABAP] Fill table rows into range table

DATA(pernrs) = VALUE pernr_tab( ( |00000001| )
                                ( |00000002| )
                                ( |00000003| ) ).

DATA(lr_pernr) = VALUE cchry_pernr_range( FOR pernr IN pernrs ( sign   = 'I'
                                                                option = 'EQ'
                                                                low    = pernr
                                                                high   = '' ) ).
"Append row to range

APPEND VALUE #( option = 'EQ'
                sign   = 'I'
                low    = pernr ) TO lr_pernr.

[ABAP] Read IT0008 lgart values

Oldschool abap…

  DATA: BEGIN OF i0008,               
        lgart LIKE p0008-lga01,                         
        betrg LIKE p0008-bet01,
        anzhl LIKE p0008-anz01,
        eitxt LIKE p0008-ein01,
        opken LIKE p0008-opk01,
        indbw LIKE p0008-ind01,                                  
        END OF i0008.

  rp-provide-from-last p0008 space pn-begda pn-endda.    

  DO 40 TIMES                                                   
           VARYING i0008-lgart FROM p0008-lga01 NEXT p0008-lga02
           VARYING i0008-betrg FROM p0008-bet01 NEXT p0008-bet02
           VARYING i0008-anzhl FROM p0008-anz01 NEXT p0008-anz02
           VARYING i0008-eitxt FROM p0008-ein01 NEXT p0008-ein02
           VARYING i0008-opken FROM p0008-opk01 NEXT p0008-opk02
           VARYING i0008-indbw FROM p0008-ind01 NEXT p0008-ind02.
      IF i0008-lgart = '2001'.
        EXIT.
      ENDIF.
  ENDDO.

[ABAP] Radiobutton with label (comment) and input field

SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE text-b01.

* Schnittstelle
SELECTION-SCREEN BEGIN OF LINE.
PARAMETERS: p_server RADIOBUTTON GROUP rad1.
SELECTION-SCREEN COMMENT 6(26) text-t01 FOR FIELD p_server.
PARAMETERS: p_sname(8) TYPE c DEFAULT ''. 
SELECTION-SCREEN END OF LINE.

* Filename (optional)
PARAMETERS: p_fname TYPE j_3sdsn.

* Lokale Datei
PARAMETERS: p_local  RADIOBUTTON GROUP rad1 DEFAULT 'X'.
* Dateiauswahl
PARAMETERS: p_up     TYPE dxfile-filename DEFAULT ''.

SELECTION-SCREEN END OF BLOCK b1.

[ABAP] Read SO10 Standard Text with Textsymbol replacement

METHOD read_text.

    DATA: lines   TYPE TABLE OF tline,
          header  TYPE THEAD,
          lt_text TYPE soli_tab.

    CALL FUNCTION 'READ_TEXT'
      EXPORTING
        id                      = 'ST'
        language                = sy-langu
        name                    = "Textname"
        object                  = 'TEXT'
      IMPORTING
        header                  = header
      TABLES
        lines                   = lines
      EXCEPTIONS
        id                      = 1
        language                = 2
        name                    = 3
        not_found               = 4
        object                  = 5
        reference_check         = 6
        wrong_access_to_archive = 7
        OTHERS                  = 8.
    IF sy-subrc <> 0.
    ENDIF.

   CALL FUNCTION 'INIT_TEXTSYMBOL'.

   CALL FUNCTION 'SET_TEXTSYMBOL'
      EXPORTING
        name    = '&MATNR&'
        value   = '00000001'
        replace = 'X'.

    CALL FUNCTION 'REPLACE_TEXTSYMBOL'
      EXPORTING
        endline   = lines( lines )
        startline = 1
      TABLES
        lines     = lines.

    CALL FUNCTION 'CONVERT_ITF_TO_STREAM_TEXT'
      EXPORTING
        language    = sy-langu
      TABLES
        itf_text    = lines
        text_stream = lt_text.

  ENDMETHOD.

[ABAP] Read smartform textmodule

Oldschool:

    DATA(ls_languages) = VALUE ssfrlang( langu1 = sy-langu ).
    DATA(lt_text_stream) = VALUE soli_tab( ).

    CALL FUNCTION 'SSFRT_READ_TEXTMODULE'
      EXPORTING
        i_textmodule       = 'Z_SMARTFORM_TEXT'
        i_languages        = ls_languages
      IMPORTING
        o_text             = lt_text
      EXCEPTIONS
        error              = 1
        language_not_found = 2
        OTHERS             = 3.

    IF lt_text IS NOT INITIAL.

      CALL FUNCTION 'CONVERT_ITF_TO_STREAM_TEXT'
        EXPORTING
          language    = sy-langu
        TABLES
          itf_text    = lt_text
          text_stream = lt_object_content.

    ENDIF.

Newschool:

    TRY.
        DATA(lr_form) = NEW cl_ssf_fb_smart_form( ).
        lr_form->load( im_formname = 'ZECOS_GM_ERROR' ).

        DATA(ls_varheader) = lr_form->varheader[ 1 ].
        DATA(lr_node) = CAST cl_ssf_fb_node( ls_varheader-pagetree ).
        DATA(lr_text) = CAST cl_ssf_fb_text_item( lr_node->obj ).

        LOOP AT lr_text->text INTO DATA(ls_text).
          WRITE:/ ls_text-tdline.
        ENDLOOP.

      CATCH cx_ssf_fb.
      CATCH cx_sy_itab_line_not_found.
    ENDTRY.

[Fiori] Send notification mail if workitem is forwarded in MyInbox

Workitem function modules

If you forward a workitem manually from the MyInbox or via function module SAP_WAPI_FORWARD_WORKITEM, the function module SWW_WI_FORWARD will be called. There the SAP implemented a BAdI call of the BAdI WF_WI_FORWARD.

BAdI WF_WI_FORWARD

The BAdI provides a method to add additional checks, when forwarding a workitem. I will use it, to send notification mails.

BAdI Filter

Create your own BAdI implementation and add a task id as filter. In my case it’s the task TS21500003 of the leave request approval workflow.
Now your implementation will only be called, if this specific workitem is forwarded. Now we implement the method CHECK_BEFORE_FORWARD. The method has enough parameters to get all necessary information to enrich the mail text.

IF_EX_WF_WI_WORKITEM~CHECK_BEFORE_FORWARD Paramter

First the workitem container is read out of the context. There we get the _WI_OBJECT_ID element, which contains the request object reference. With this information we are able to get the current request object out of the workflow. I pass this into antother class where I already have a mail sending implementation.

  METHOD if_ex_wf_wi_workitem~check_before_forward.
*---------------------------------------------------------------------------------*
* This BAdI implementation is used to send an info mail when a workitem is forwarded.
*---------------------------------------------------------------------------------*

    TRY.

        "Get workitem container and requestId
        DATA(container) = im_workitem_context->get_wi_container( ).

        container->get_value_ref( EXPORTING name       = |_WI_OBJECT_ID|
                                  IMPORTING value_ref  = DATA(lr_req_id) ).

        ASSIGN lr_req_id->* TO FIELD-SYMBOL(<lpor>).

        "Get current request object
        DATA(lo_req) = NEW cl_pt_req_wf_attribs( )->bi_persistent~find_by_lpor( lpor = <lpor> ).

        "Send an info mail to each new agent (should be only one)
        LOOP AT im_table_new_agents INTO DATA(new_agent).
          zcl_hcm_leave_request_assist=>send_mail( io_req         = CAST cl_pt_req_wf_attribs( lo_req )
                                                   iv_tdname      = mc_mailtext
                                                   iv_pernr       = cl_hcmfab_employee_api=>get_instance( )->get_employeenumber_from_user( iv_user = new_agent-objid ) ).
        ENDLOOP.


      CATCH cx_hcmfab_common.
      CATCH cx_swf_cnt_elem_not_found.
      CATCH cx_swf_cnt_container.
        "In error case, do nothing. The workitem should still be forwarded.
        RETURN.
    ENDTRY.

    "Write Info to WF Log
    MESSAGE s001(00) WITH |Forward: Mail { mc_mailtext }| INTO DATA(lv_message).
    im_workitem_context->set_message_to_log( im_function = CONV #( lv_message )         "max char30
                                             im_message = VALUE #( msgid = sy-msgid
                                                                   msgty = sy-msgty
                                                                   msgno = sy-msgno
                                                                   msgv1 = sy-msgv1 ) ).
    COMMIT WORK.
  ENDMETHOD.

At the end I’m writing a little notification in the workflow log. The workitem context provides the method set_message_to_log for this. The log will look like this.

[ABAP] Access leave request attributes

cl_pt_req_badi=>get_request( EXPORTING im_req_id  = request_id
                             IMPORTING ex_request = DATA(request) ).

request->get_all_attribs( IMPORTING ex_attribs_struc = DATA(req_attribs) ).

DATA(req_begda) = req_attribs-version-item_tab[ 10 ]-value.

Take a look in the item_tab to see the specific request values.

[ABAP] Add custom message in workflow log

In a function exit of a workflow task, you can copy the sample coding out of class CL_SWH_WORKITEM_EXIT_LOG to append a custom message

METHOD if_swf_ifs_workitem_exit~event_raised .
  DATA: ls_msg TYPE swr_mstruc.
  DATA: ls_por TYPE sibflporb.
  DATA: l_cnt TYPE REF TO if_swf_ifs_parameter_container.

  me->m_ctx = im_workitem_context.
  IF im_event_name EQ swrco_event_after_creation.
    ls_msg-msgid = 'SWH'.
    ls_msg-msgty = 'S'.
    ls_msg-msgno = '100'.
    ls_msg-msgv1 = 'CL_SWH_WORKITM_EXIT_LOG'.
    ls_msg-msgv2 = 'swrco_event_after_creation'.
    l_cnt = m_ctx->get_wi_container( ).
    TRY.
        CALL METHOD l_cnt->get
          EXPORTING
            name  = swrco_wi_leading_object
          IMPORTING
            value = ls_por.
        CALL METHOD m_ctx->set_message_to_log
          EXPORTING
            im_message    = ls_msg
            im_object_por = ls_por.
      CATCH cx_swf_cnt_container .
    ENDTRY.
  ENDIF.
ENDMETHOD.                    "IF_SWF_IFS_WORKITEM_EXIT~EVENT_RAISED

But to add a message in general, only the workitem context is needed. If you got the context, just use method set_message_to_log.

* context type: im_workitem_context TYPE REF TO if_wapi_workitem_context   

" text in im_function will be displayed in workitem log (max char30),
" message in im_message will give further information when clicking on the traffic light (max char 70).
im_workitem_context->set_message_to_log( im_function = |Forward: Mail { var }|
                                         im_message = VALUE #( msgid = '00'
                                                               msgty = 'S'
                                                               msgno = '000'
                                                               msgv1 = |var 1|
                                                               msgv2 = |var 2| ) ).
* In some cases "COMMIT WORK" is needed.

You’ll find the log entry in workitem flog view: