nocin.eu

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

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.

[Hardware] ASUS TUF GAMING B550M-PLUS (WIFI) – Bluetooth broken

Somehow the onboard Bluetooth of my Motherboard stopped working, but the Wi-Fi was still working fine. This confused me because both are provided by the Intel Wi-Fi 6 AX200 card. The blueman-manager simply could not find the Bluetooth device anymore. Also, when running the command rfkill, it was just listing the Wi-Fi device. The second entry was missing (I took the screenshot, after Bluetooth was working again).

Since I had recently upgraded to a new kernel, I assumed this must be the problem. After testing many different kernels, it still didn’t work. Even on a kernel where it definitely worked before.

Then I stumbled across this thread Intel Wi-Fi 6 AX200 Bluetooth Stopped Working Yet Wifi is still fine. The last commenter solved his issue by unplugging the Intel Wi-Fi Card from his Motherboard and plugging it back in. To be honest, I’m not even sure if this is possible on my motherboard, as it is some onboard thing directly on the board. But this post leads me to reconnect the external antenna, which is connected with two cables on the back of the PC. Since Wi-Fi was still working all along, I did not expect anything from it, but after turning the PC back on, suddenly Bluetooth was working again. WTF! No idea how this is possible, as I have used Bluetooth and WI-FI for many months without the external antenna. Fortunately, however, it works again.

[HR] Abwesenheiten (IT2001) – Anzeige Kontingentabtragungen

Bei einer Abwesenheit wie z.B. Urlaub, die über eine Monatsgrenze oder über einige Feiertage hinweg geht, kann es hilfreich sein, die konkreten Abtragungen einzusehen, um zu verstehen, welche Tage in dem Zeitraum denn wirklich Urlaub waren.

Dazu einfach die Abwesenheit öffnen in der PA20 und auf Springen → Abtragungen (Shift+F8) gehen.

Man kann die Kontingentabtragung auch für das komplette Kontingent eines Jahres einsehen via IT2006.

Die zugehörige Datenbanktabelle ist PTQUODED. Um die Abträge für eine bestimmte Abwesenheitsart zu selektieren, muss noch ein JOIN auf die PA2001 über die DOCNR gemacht werden. Zumindest ist das der einzige Weg, den ich herausfinden konnte. 🙂

SELECT SUM( a~quode )
    FROM ptquoded AS a
    JOIN pa2001   AS b
      ON a~pernr = b~pernr
     AND a~docnr = b~docnr
    INTO DATA(urlaubsabtrag)
    WHERE a~pernr = pernr-pernr
     AND a~datum BETWEEN pn-begda AND pn-endda
     AND b~subty = 0100. " Abwesenheitsart, welche Selektiert werden soll