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

[ABAP] Read ACTION_COMMENTS from a Workitem

If you want to use the decision note / action comment of a Workitem in a mail step, you have to write a method to read the comment and then pass it to the mail step. To do this, I always create a method in my Workflow helper class called READ_DECISION_NOTE with the following parameters:

For the importing parameter, you must store the Workitem ID of your Decision Step in you Workflow Container, by creating a Container Element like DecisionWIID of type SWW_WIID and fill it with the Workitem ID of your decision step. Then use it later for calling your method READ_DECISION_NOTE.

Regarding the exporting parameter; If you want to send a mail via class cl_cbs you’re a fine reading the comment as string. But if you want to use the default workflow mail step, and you are expecting comments with more than >255 characters, you will need the comment as SOLI_TAB.

If you take a look at the decision Workitem, you will properly find the Attribute ACTION_COMMENTS, which is of type SWC_VALUE and is of type char with length 255. But this is only the case, if the entered text has less than < 255 characters.

If the user entered a text with more than 255 characters, the text is split and there are more elements named with ACTION_COMMENTS_1, ACTION_COMMENTS_2 etc. (Note 3017539)

There is the function module SAP_WAPI_READ_CONTAINER to read Workitems. As input for the function module, you need the Workitem ID from the decision step. You will then find the ACTION_COMMENTS in the simple_container table.

    DATA: return_code              TYPE sy-subrc,
          simple_container         TYPE TABLE OF swr_cont,
          message_lines            TYPE TABLE OF swr_messag,
          message_struct           TYPE TABLE OF swr_mstruc,
          subcontainer_bor_objects TYPE TABLE OF swr_cont,
          subcontainer_all_objects TYPE TABLE OF swr_cont,
          object_content           TYPE TABLE OF solisti1.

    " Read the work item container from the work item ID
    CALL FUNCTION 'SAP_WAPI_READ_CONTAINER'
      EXPORTING
        workitem_id              = iv_wiid
      IMPORTING
        return_code              = return_code
      TABLES
        simple_container         = simple_container
        message_lines            = message_lines
        message_struct           = message_struct
        subcontainer_bor_objects = subcontainer_bor_objects
        subcontainer_all_objects = subcontainer_all_objects.


    TRY.
        DATA(text) = simple_container[ element = 'ACTION_COMMENTS' ]-value.
      CATCH cx_sy_itab_line_not_found.
      " Check for ACTION_COMMENTS_1 etc.
      " or follow the approach below
    ENDTRY.

The comment is also added as SOFM-Object (SAP-Office-Document) to the Workitem. Just check the table subcontainer_all_objects, which is also returned by the previous function module, for attribute _ATTACH_COMMENT_OBJECTS (or _ATTACH_OBJECTS or DECISION_NOTE or _DECISION_COMMENT). With function module SO_DOCUMENT_READ_API1 you can then get the actual comment.

    " Read the _ATTACH_COMMENT_OBJECTS element
    " There can be more than one comment, just take the last one
    LOOP AT subcontainer_all_objects INTO DATA(comment_object) WHERE element = '_ATTACH_COMMENT_OBJECTS'.
    ENDLOOP.

    CHECK comment_object-value IS NOT INITIAL.

    " Read the SOFM Document
    CALL FUNCTION 'SO_DOCUMENT_READ_API1'
      EXPORTING
        document_id    = CONV so_entryid( comment_object-value )
      TABLES
        object_content = object_content
      EXCEPTIONS
        OTHERS         = 1.

    et_decision_note = object_content.

* Or create a single string
*    LOOP AT object_content INTO DATA(lv_soli).
*      CONCATENATE text lv_soli-line INTO text.
*    ENDLOOP.

This SOLI_TAB can be passed to a default mail step and will keep line breaks etc.

Update 02.06.2026: A shorter approach can be the following.

    DATA(lo_workitem) = NEW /iwwrk/cl_wf_read_workitem( iv_wiid ).

    lo_workitem->get_wi_appr_comments( IMPORTING et_wi_appr_comm = DATA(lt_wi_appr_comm)
                                                 ev_return_code  = DATA(lv_return_code) ).

    READ TABLE lt_wi_appr_comm INTO DATA(ls_decision) WITH KEY obj_name = 'COMMENT' obj_type = 'TXT'.

    DATA(soli_tab) = cl_document_bcs=>string_to_soli( ls_decision-text ).

But string to soli_tab is always a bit risky when having a text with >255 characters (see here).

[SAPUI5] Get i18n texts

To simply get access to i18n texts, I useally add this helper function to my BaseController.js

// helper for direct access to the ResourceBundle getText() function
getText : function (sTextKey, aParamter) {
    return this.getOwnerComponent().getModel("i18n").getResourceBundle().getText(sTextKey, aParamter)
}

Texts can then be read in every controller with

// i18n: objects=Amount of objects: {0}
this.getText("objects", [iLength])

[CAP] BAS – port 4004 is already in use by another server process

If the default port 4004 is already open and you want to see what is bound to it, select View -> Find Command -> Ports Preview

Solution to kill another “watch.js” process using/blocking the port: https://answers.sap.com/questions/13016130/sap-business-application-studio-stop-running-serve.html
To archive the same in a single command use:

lwctl -c basic-tools kill -9 $(lwctl -c basic-tools ps aux | grep watch.js | awk '{print $2}')

As alternative, change the default port by adding a new port in package.json to the start script, for example: “start”: “cds run –port 4003” and use npm run start instead of cds watch.


Update 20.02.2023: Just had the problem again due to a VPN disconnect. But this time I had an application running using cds run. Therefore, I had to change the command from watch.js to cds.js:

lwctl -c basic-tools kill -9 $(lwctl -c basic-tools ps aux | grep cds.js | awk '{print $2}')

Update 10.05.2023: A better approach seems to be killing the node process. This should work in both situation.

lwctl -c basic-tools kill -9 $(lwctl -c basic-tools ps aux | grep node | awk '{print $2}')

Update 29.02.2024: With the BAS migration to Code – OSS the previous commands were not working anymore, but this new command seems to work:

kill -9 $(ps aux | grep cds.js | awk '{print $2}')
# or
kill -9 $(ps aux | grep cds-dk | awk '{print $2}')

Update 15.07.2025:

kill -9 $(ps aux | grep cds | awk '{print $2}')

[CAP] Query teamMembersSize from SuccessFactors

const sfsfSrv = await cds.connect.to('sfsf')

// Option 1: Query Notation
const response = await sfsfSrv.run(SELECT`teamMembersSize`.from`User`.where`userId = ${req.user.id}`)
console.log("option 1: " + response[0].teamMembersSize)

// Option 2: HTTP method-style
const response2 =  await sfsfSrv.get(`/User('${req.user.id}')/teamMembersSize/$value`)
console.log("option 2: " + response2)

[CAP] Easy way to test if your approuter is working

An easy way to test your approuter is using the user-api-service. For that add the following route to your xs-app.json

    {
        "source": "^/user-api(.*)",
        "target": "$1",
        "service": "sap-approuter-userapi"
    }

And after deployment, open the application router URL and add /user-api/currentUser to it. You should see your Email and other User details. This is testing that the application router is actually getting the security token from the UAA instance.