TRY.
DATA(messsage_handler) = NEW cl_hrpa_message_list( ).
cl_hrpa_masterdata_enq_deq=>enqueue_by_pernr( tclas = cl_hrpa_tclas=>tclas_employee
pernr = 1
message_handler = messsage_handler ).
cl_hrpa_masterdata_enq_deq=>dequeue_by_pernr( tclas = cl_hrpa_tclas=>tclas_employee
pernr = 1 ).
CATCH cx_hrpa_violated_assertion.
ENDTRY.
Tag: abap
[ABAP] Escape URL
DATA(path) = escape( val = 'https://url.com/path/with/a space/in/it'
format = cl_abap_format=>e_url ).
[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 ).
Instead of SO_SPLIT_FILE_AND_PATH
you can also use PC_SPLIT_COMPLETE_FILENAME
.
[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.
[ABAP] Get the last day of the previous month
I recently had to find the last day of the previous month. I found the Method get_last_day_prev_month
of class cl_bs_period_toolset_basics
doing the job. The actual logic is super simple:
rv_date = iv_date.
rv_date+6(2) = '01'.
rv_date = rv_date - 1.
That’s why I thought about whether I needed a separate method at all, or whether I could do it directly in a one-liner. My first idea was to use the LET operator in combination with CONV (Option 2). This works, but does not look very intuitive, and I noticed it can be done even simpler without using LET (Option 3). And if you already have a typed variable, you can do it even shorter with a single CONV (Option 4).
* Option 1
DATA(date_1) = cl_bs_period_toolset_basics=>get_last_day_prev_month( sy-datum ).
*Option 2
DATA(date_2) = CONV d( LET x = CONV d( sy-datum(6) && '01' ) IN x - 1 ).
*Option 3
DATA(date_3) = CONV d( CONV d( sy-datum(6) && '01' ) - 1 ).
* Option 4
DATA date_4 TYPE datum.
date_4 = CONV d( sy-datum(6) && '01' ) - 1.
cl_demo_output=>write_data( date_1 ).
cl_demo_output=>write_data( date_2 ).
cl_demo_output=>write_data( date_3 ).
cl_demo_output=>write_data( date_4 ).
cl_demo_output=>display( ).
All in all, I have to say that using the get_last_day_prev_month
method is still the best solution as it is much more readable.
[ABAP] Fill range table directly from select statement
SELECT @if_fsbp_const_range=>sign_include AS sign,
@if_fsbp_const_range=>option_equal AS option,
bukrs AS low,
CAST( @space AS CHAR( 4 ) ) AS high
FROM t001
INTO TABLE @DATA(range_of_comp_codes).
cl_demo_output=>display( range_of_comp_codes ).
Got this snippet from George Drakos talk from the ABAPConf 2024. Check his Github.
[HR] A1-Meldeverfahren
Übersicht
Gute Zusammenfassung: https://www.iprocon.de/abbildung-des-a1-meldeverfahrens-in-sap-hcm/
2682093 – Informationen zur Umsetzung des A1-Meldeverfahrens im SAP-System (siehe auch das PDF im Hinweis)
2730927 – A1-Verfahren: Hilfestellung zum Customizing
2841779 – A1-Verfahren: ESS-Szenario (Web-Dynpro ABAP) (Paket PAOC_ESS_A1_DE)
Die Datenerfassung der Antragsdaten erfolgt im Infotyp: 0700 (Elektronischer Datenaustausch)
Subtypen:
- DXA1 (A1: Antrag Entsendebescheinigung)
- DXAV (A1: Antrag Ausnahmevereinbarung)
Reports zur Erstellung der Meldungen und der Meldedateien:
- Personal → Personalabrechnung → Europa → Deutschland → Folgeaktivitäten → Periodenunabhängig → Abrechnungszusatz → A1-Meldeverfahren → Ausgangsmeldungen
Reports zur Verarbeitung der Eingangsmeldungen:
- Personal → Personalabrechnung → Europa → Deutschland → Folgeaktivitäten → Periodenunabhängig → Abrechnungszusatz → A1-Meldeverfahren → Eingangsmeldungen
Behördenkommunikation (B2A): Transaktion PB2A
- Personal → Personalabrechnung → Europa → Deutschland → Folgeaktivitäten → Periodenunabhängig → Behördenkommunikation (B2A) → B2A-Manager
Hilfreiche Entwicklungsobjekte
Paket: P01S
Reports:

Klassen:

Für mich hilfreich waren folgenden Klassen:
- CL_HRPAYDE_A1_NOTIF (Klasse für A1-Meldungen)
- CL_HRPAYDE_A1_NOTIF_DISPLAYER (ALV-Ausgabe für A1-Meldungen)
- CL_HRPAYDE_A1_ALV_EVNT_HANDLER (Ereignisbehandler für A1-spezifische Ereignisse)
PDFs der A1-Meldungen von Datenbank lesen
*&---------------------------------------------------------------------*
*& Tabellen A1-Meldeverfahren:
*& P01A1_STAT - A1-Verfahren: Verwaltungstabelle
*& P01A1_RAWDATA - A1-Verfahren: Rohdaten einer Meldung
*&---------------------------------------------------------------------*
GET peras.
SELECT * INTO TABLE @DATA(lt_p01a1_stat) FROM p01a1_stat
WHERE pernr = @pernr-pernr
AND mdtyp = '2'
AND status = '23'
AND mzbeg <= @pn-endda
AND mzbeg >= @pn-begda.
LOOP AT lt_p01a1_stat INTO DATA(ls_p01a1_stat).
SELECT SINGLE * INTO @DATA(ls_p01a1_rawdata) FROM p01a1_rawdata
WHERE guid = @ls_p01a1_stat-guid
AND lfdnr = ( SELECT MAX( lfdnr ) FROM p01a1_rawdata WHERE guid = @ls_p01a1_stat-guid ). "höchste lfdnr nehmen
DATA(pdf_xstring) = ls_p01a1_rawdata-rawdata.
ENDLOOP.
[ABAP] REDUCE as an alternative to line_exists for the use of other comparison operators such as >, <, >=, <=, CS, CN
By using line_exists
you can check if a line exists or not exists, but it is not possible to use other comparison operators. A workaround can be REDUCE
in that case.
In the following snippet, we want to check if there is a flight that costs more than $1000,
SELECT * FROM sflight INTO TABLE @DATA(flights).
* Check if flight with price > 1000 exist
IF REDUCE abap_bool( INIT price_gt_1000 = abap_false
FOR line IN flights WHERE ( price > 1000 )
NEXT price_gt_1000 = abap_true ) EQ abap_true.
" flight with price > 1000 exists
ENDIF.