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

[ABAP] Validate date

CALL FUNCTION 'DATE_CHECK_PLAUSIBILITY'
  EXPORTING
    date                      = '20250231' " invalid date
  EXCEPTIONS
    plausibility_check_failed = 1
    OTHERS                    = 2.
IF sy-subrc <> 0.
  MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
ENDIF.

If you’re not interested in a specific error message, you can also use the following method to get the result as boolean.

DATA(date_is_valid) = cl_rs_time_service=>is_valid_date( '20250231' ).

[ABAP] BASE CORRESPONDING

I recently had to rewrite a Report where I saw code that looked similar to this.

TYPES:

  BEGIN OF ty_employee,
    vorna TYPE vorna,
    nachn TYPE nachn,
  END OF ty_employee,
  tt_employee TYPE TABLE OF ty_employee WITH DEFAULT KEY,

  BEGIN OF ty_source,
    col1 TYPE c LENGTH 2,
    col2 TYPE c LENGTH 2,
    col3 TYPE c LENGTH 2,
    col4 TYPE c LENGTH 2,
  END OF ty_source,
  tt_source TYPE TABLE OF ty_source WITH DEFAULT KEY,

  BEGIN OF ty_target,
    col1       TYPE c LENGTH 2,
    col2       TYPE c LENGTH 2,
    col3       TYPE c LENGTH 2,
    col4_vorna TYPE vorna,
    col5_nachn TYPE nachn,
  END OF ty_target,
  tt_target TYPE TABLE OF ty_target WITH DEFAULT KEY.

* Test employees
DATA(employees) = VALUE tt_employee( ( vorna = 'Max'   nachn = 'Mustermann' )
                                     ( vorna = 'Erika' nachn = 'Mustermann' ) ).

* In reallity, this table changed per employee
DATA(source_tab) = VALUE tt_source( ( col1 = 'a1' col2 = 'b1' col3 = 'c1' col4 = 'd1' )
                                    ( col1 = 'a2' col2 = 'b2' col3 = 'c2' col4 = 'd2' )
                                    ( col1 = 'a3' col2 = 'b3' col3 = 'c3' col4 = 'd3' ) ).

* Actual logic
DATA target_line TYPE ty_target.
DATA target_tab1 TYPE tt_target.

LOOP AT employees INTO DATA(employee).
  LOOP AT source_tab INTO DATA(source_line).
    CLEAR target_line.
    target_line-col4_vorna = employee-vorna.
    target_line-col5_nachn = employee-nachn.
    MOVE-CORRESPONDING source_line TO target_line.
    APPEND target_line TO target_tab1.
  ENDLOOP.
ENDLOOP.

cl_demo_output=>write( employees ).
cl_demo_output=>write( source_tab  ).
cl_demo_output=>write( target_tab1  ).
cl_demo_output=>display( ).

I spend some time thinking, how I could improve the actual logic by using some “new” ABAP syntax elements. I came up with the following result.

DATA target_tab2 TYPE tt_target.

LOOP AT employees INTO employee.
  target_tab2 = VALUE #( BASE CORRESPONDING #( target_tab2 )
                         FOR line IN source_tab (
                          VALUE #( BASE CORRESPONDING #( line )
                           col4_vorna = employee-vorna
                           col5_nachn = employee-nachn ) ) ).
ENDLOOP.

But I must say, that in this case I prefer using nested LOOP’s like this

DATA target_tab3 TYPE tt_target.

LOOP AT employees INTO employee.
  LOOP AT source_tab INTO source_line.
    DATA(t_line)      = CORRESPONDING ty_target( source_line ).
    t_line-col4_vorna = employee-vorna.
    t_line-col5_nachn = employee-nachn.
    APPEND t_line TO target_tab3.
  ENDLOOP.
ENDLOOP.

or this

DATA target_tab4 TYPE tt_target.

LOOP AT employees INTO employee.
  LOOP AT source_tab INTO source_line.
    APPEND VALUE #( BASE CORRESPONDING #( source_line )
                    col4_vorna = employee-vorna
                    col5_nachn = employee-nachn ) TO target_tab4.
  ENDLOOP.
ENDLOOP.

[ABAP] E-Mail-Templates in S/4HANA

Recently I had to create a mail process where I used the first time the “new” E-Mail-Templates. The following Blogs helped me do this:

https://weberpatrick.de/e-mail-templates/
https://weberpatrick.de/e-mail-templates-einfaches-beispiel/
https://weberpatrick.de/e-mail-template-daten-per-code-statt-cds/

https://community.sap.com/t5/enterprise-resource-planning-blogs-by-members/e-mail-templates-in-s-4-hana/ba-p/13397719
https://community.sap.com/t5/enterprise-resource-planning-blogs-by-members/e-mail-templates-in-s-4-hana-translations/ba-p/13440792
https://community.sap.com/t5/enterprise-resource-planning-blogs-by-members/e-mail-templates-in-s-4-hana-display-table-in-email-template/ba-p/13546304

https://www.solviads.com/e-mail-template-in-sap-s-4-hana

In my case it was not possible to fetch the data via CDS View, I therefore had to manually fill the variables. Following a simplified version of my code.

You must create a CDS View, as this is the structure in which the variables defined in the mail text are mapped to, even if you do this manually.

define view entity ZMAIL_MY_TEMPLATE as select from pa0001 {
    key pa0001.pernr,
    pa0001.ename,
    cast ( '' as abap.char( 20 ) ) as custom_field // this way you can add a field, that does not exist in the selected table
}

This is just one way, how you can manually fill the variables. Other ways are possible using the class cl_smtg_email_api. I choose the method render_w_data because it returned the mail text, and you manually can do additional changes to the mail text, like adding HTML-Table content, which otherwise gets “escaped” when filled via a replacement variable.

    " dynamicaly build table for mail variables
    DATA(lt_comp) = VALUE abap_component_tab( ( name = 'ename' type = CAST #( cl_abap_typedescr=>describe_by_name( 'EMNAM'   ) ) )
                                              ( name = 'pernr' type = CAST #( cl_abap_typedescr=>describe_by_name( 'PERNR_D' ) ) ) ) .

    " fill table with concrete data
    DATA(lo_struct) = cl_abap_structdescr=>get( lt_comp ).
    CREATE DATA mr_data TYPE HANDLE lo_struct.
    ASSIGN mr_data->* TO FIELD-SYMBOL(<ls_data>).
    <ls_data>-(1) = ename.
    <ls_data>-(2) = pernr.

    " create instance of eMail API, provide name of Mail template
    DATA(lo_email_api) = cl_smtg_email_api=>get_instance( iv_template_id = 'ZMAIL_MY_TEMPLATE' ).

    " render E-Mail for CL_BCS class. This replaces all placeholders with real data
    lo_email_api->render_w_data( EXPORTING iv_language  = sy-langu
                                           ir_data      = mr_data
                                 IMPORTING ev_subject   = DATA(subject)
                                           ev_body_html = DATA(body_html) ).

    TRY.
        " create Sender & Receiver
        DATA(lo_sender)    = cl_cam_address_bcs=>create_internet_address( i_address_string = 'noreply@test.de'
                                                                          i_address_name   = 'Test' ).
        DATA(lo_recipient) = cl_cam_address_bcs=>create_internet_address( i_address_string = 'receiver@test.de').

        " create mail document
        DATA(lo_mail_document) = cl_document_bcs=>create_document( i_type    = 'HTM'
                                                                   i_subject = CONV #( subject )
                                                                   i_text    = cl_bcs_convert=>string_to_soli( body_html ) ).

        " create Business Communication Service
        DATA(lo_bcs) = cl_bcs=>create_persistent( ).
        lo_bcs->set_document( lo_mail_document ).
        lo_bcs->set_sender( lo_sender ).
        lo_bcs->add_recipient( lo_recipient ).
        lo_bcs->send( ).
        COMMIT WORK.                                       
      CATCH cx_smtg_email_common INTO DATA(ls_cx).
        DATA(lv_message) = ls_cx->get_text( ).
        MESSAGE s899(id) WITH 'Unable to send message:'(004) lv_message.
    ENDTRY.

[VSC] Get X-CSRF-Token via REST Client

### Get CSRF-Token
# @name tokenResponse
HEAD https://url.com/api/endpoint HTTP/1.1
Authorization: Basic {{$dotenv auth_base64}}
x-csrf-token: Fetch

@x-csrf-token = {{ tokenResponse.response.headers.x-csrf-token }}   


### Use Token
POST https://url.com/api/endpoint HTTP/1.1
Authorization: Basic {{$dotenv auth_base64}}
Content-Type: application/json
x-csrf-token: {{x-csrf-token}}

{
      "data" : 1
}

[Git] Bundle/export a git repository as a single file

# run command inside the root directory of the project to bundle including all branches
git bundle create reponame.bundle --all

#  to bundle only the main branch
git bundle create reponame.bundle main

# to unbundle, use the following command
git clone reponame.bundle

# after cloning, you will see the bundle is set as default remote repository origin
git remote -v
> origin  /home/user/projects/reponame.bundle (fetch)
> origin  /home/user/projects/reponame.bundle (push)

# if you want to add a new remote repo as origin, you first have to remove the current origin repository, but this means loosing access to your branches etc.
git remote rm origin

# alternatively, you can provide a new name while cloning
git clone --origin <new_name> reponame.bundle

[Windows] Snipping Tool – Shortcuts

Win + Shift + S to take a screenshot of a specific area

Win + Shift + R to create a short video

Bonus: Holding Alt while pressing Print will only take a screenshot of the current active window.
And you can also use Win + V to set up clipboard history so you can take 3 screenshots at once then post them all at the same time.