DATA(path) = escape( val = 'https://url.com/path/with/a space/in/it'
format = cl_abap_format=>e_url ).
Year: 2025
[ABAP] Get Filename and Mimetype from uploaded file
DATA data_tab TYPE solix_tab.
DATA filename TYPE string.
DATA path TYPE string DEFAULT 'C:\Users\path\to\my\file.pdf'.
cl_gui_frontend_services=>gui_upload( EXPORTING filename = path )
filetype = 'BIN'
CHANGING data_tab = data_tab ).
CALL FUNCTION 'SO_SPLIT_FILE_AND_PATH'
EXPORTING
full_name = path
IMPORTING
stripped_name = filename
EXCEPTIONS
x_error = 1
OTHERS = 2.
DATA(file_extension) = /iwwrk/cl_mgw_workflow_rt_util=>get_extention_from_file_name( filename ).
DATA(mimetype) = /iwwrk/cl_mgw_workflow_rt_util=>get_mime_type_from_extension( file_extension ).
[Home Assistant] Editor shortcuts
All VS Code shortcuts will also work in Home Assistant. I mostly need the following:
Tab | Move lines to right |
Ctrl + Tab (on Linux Mint it’s Shit + Tab) | Move lines to left |
Ctrl + Alt + Mouse selection | Mark area over multiple lines (works only in YAML editor) |
Ctrl + Shift + K | Delete row |
Alt + Arrow key up or down | Move row(s) up or down |
Alt + Shift + Arrow down | Duplicate selected rows |
Ctrl + Shit + / | Comment line/area |
Also, you can simply expand the window you are working in, by clicking on the window title.


[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] Validate pernr
DATA ls_return TYPE bapireturn.
CALL FUNCTION 'BAPI_EMPLOYEE_CHECKEXISTENCE'
EXPORTING
number = 99999999
IMPORTING
return = ls_return.
IF ls_return-type = 'E'.
MESSAGE ls_return-message TYPE ls_return-type.
ENDIF.
[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