Function Modules (Funktionsbausteine) sind wiederverwendbare ABAP-Routinen mit definierter Schnittstelle. Sie werden in Funktionsgruppen organisiert und können RFC-fähig für Remote-Aufrufe sein.
Grundkonzept
| Element | Beschreibung |
|---|---|
| Funktionsgruppe | Container für Function Modules |
| IMPORTING | Eingabeparameter |
| EXPORTING | Ausgabeparameter |
| CHANGING | Ein-/Ausgabeparameter |
| TABLES | Tabellenparameter (veraltet) |
| EXCEPTIONS | Fehlerbehandlung |
Syntax
CALL FUNCTION 'FUNCTION_NAME' EXPORTING param1 = value1 IMPORTING result = lv_result CHANGING data = ls_data TABLES itab = lt_table EXCEPTIONS error1 = 1 error2 = 2 OTHERS = 3.
IF sy-subrc <> 0. " FehlerbehandlungENDIF.Beispiele
1. Function Module aufrufen
DATA: lv_date_out TYPE sy-datum.
" Datum berechnenCALL FUNCTION 'RP_CALC_DATE_IN_INTERVAL' EXPORTING date = sy-datum days = 30 months = 0 sigession = '+' years = 0 IMPORTING calc_date = lv_date_out.
WRITE: / 'Datum + 30 Tage:', lv_date_out.2. Function Module mit Exceptions
DATA: lv_result TYPE p DECIMALS 2.
CALL FUNCTION 'CONVERSION_EXIT_ALPHA_INPUT' EXPORTING input = '123' IMPORTING output = lv_result EXCEPTIONS invalid_input = 1 OTHERS = 2.
CASE sy-subrc. WHEN 0. WRITE: / 'Ergebnis:', lv_result. WHEN 1. WRITE: / 'Ungültige Eingabe'. WHEN OTHERS. WRITE: / 'Unbekannter Fehler'.ENDCASE.3. Eigenen Function Module erstellen
" In SE37 oder SE80:" 1. Funktionsgruppe anlegen: ZFGRP_CUSTOMER" 2. Funktionsbaustein anlegen: Z_CUSTOMER_GET_BY_ID
FUNCTION z_customer_get_by_id.*"----------------------------------------------------------------------*"*"Lokale Schnittstelle:*" IMPORTING*" VALUE(IV_KUNNR) TYPE KUNNR*" EXPORTING*" VALUE(ES_CUSTOMER) TYPE KNA1*" VALUE(EV_FOUND) TYPE ABAP_BOOL*" EXCEPTIONS*" CUSTOMER_NOT_FOUND*" INVALID_INPUT*"----------------------------------------------------------------------
" Eingabeprüfung IF iv_kunnr IS INITIAL. RAISE invalid_input. ENDIF.
" Kunde lesen SELECT SINGLE * FROM kna1 WHERE kunnr = @iv_kunnr INTO @es_customer.
IF sy-subrc = 0. ev_found = abap_true. ELSE. ev_found = abap_false. RAISE customer_not_found. ENDIF.
ENDFUNCTION.
" AufrufDATA: ls_customer TYPE kna1, lv_found TYPE abap_bool.
CALL FUNCTION 'Z_CUSTOMER_GET_BY_ID' EXPORTING iv_kunnr = '0000001000' IMPORTING es_customer = ls_customer ev_found = lv_found EXCEPTIONS customer_not_found = 1 invalid_input = 2 OTHERS = 3.
IF sy-subrc = 0. WRITE: / ls_customer-name1.ENDIF.4. RFC-fähiger Function Module
" Einstellung in SE37:" Attributes -> Processing Type: Remote-Enabled Module
FUNCTION z_rfc_get_sales_data.*"----------------------------------------------------------------------*"*"Lokale Schnittstelle:*" IMPORTING*" VALUE(IV_VKORG) TYPE VKORG*" VALUE(IV_DATE_FROM) TYPE SY-DATUM*" VALUE(IV_DATE_TO) TYPE SY-DATUM*" EXPORTING*" VALUE(ET_ORDERS) TYPE ZTT_ORDERS*" EXCEPTIONS*" NO_DATA_FOUND*" INVALID_DATE_RANGE*"----------------------------------------------------------------------
" Datumsbereich prüfen IF iv_date_from > iv_date_to. RAISE invalid_date_range. ENDIF.
" Daten lesen SELECT vbeln, erdat, kunnr, netwr FROM vbak WHERE vkorg = @iv_vkorg AND erdat BETWEEN @iv_date_from AND @iv_date_to INTO CORRESPONDING FIELDS OF TABLE @et_orders.
IF sy-subrc <> 0. RAISE no_data_found. ENDIF.
ENDFUNCTION.
" Remote-AufrufCALL FUNCTION 'Z_RFC_GET_SALES_DATA' DESTINATION 'PRD_100' " RFC-Destination EXPORTING iv_vkorg = '1000' iv_date_from = '20240101' iv_date_to = '20241231' IMPORTING et_orders = lt_orders EXCEPTIONS no_data_found = 1 invalid_date_range = 2 communication_failure = 3 MESSAGE lv_msg system_failure = 4 MESSAGE lv_msg OTHERS = 5.
IF sy-subrc >= 3. WRITE: / 'RFC-Fehler:', lv_msg.ENDIF.5. Function Module mit TABLES-Parameter
" TABLES ist veraltet, aber noch oft verwendetFUNCTION z_process_items.*"----------------------------------------------------------------------*"*"Lokale Schnittstelle:*" TABLES*" IT_ITEMS STRUCTURE ZITEM*" ET_RESULTS STRUCTURE ZRESULT*"----------------------------------------------------------------------
LOOP AT it_items. " Verarbeitung et_results-id = it_items-id. et_results-status = 'OK'. APPEND et_results. ENDLOOP.
ENDFUNCTION.
" Moderne Alternative: TYPE für TabellenparameterFUNCTION z_process_items_new.*"----------------------------------------------------------------------*" IMPORTING*" VALUE(IT_ITEMS) TYPE ZTT_ITEMS*" EXPORTING*" VALUE(ET_RESULTS) TYPE ZTT_RESULTS*"----------------------------------------------------------------------
LOOP AT it_items INTO DATA(ls_item). APPEND VALUE #( id = ls_item-id status = 'OK' ) TO et_results. ENDLOOP.
ENDFUNCTION.6. Update Task Function Module
" Für asynchrone DatenbankänderungenFUNCTION z_update_customer IN UPDATE TASK.*"----------------------------------------------------------------------*" IMPORTING*" VALUE(IS_CUSTOMER) TYPE KNA1*"----------------------------------------------------------------------
MODIFY kna1 FROM is_customer.
ENDFUNCTION.
" AufrufCALL FUNCTION 'Z_UPDATE_CUSTOMER' IN UPDATE TASK EXPORTING is_customer = ls_customer.
" Commit führt Update ausCOMMIT WORK AND WAIT.7. Function Module mit klassenbasierten Exceptions
FUNCTION z_divide_numbers.*"----------------------------------------------------------------------*" IMPORTING*" VALUE(IV_DIVIDEND) TYPE I*" VALUE(IV_DIVISOR) TYPE I*" EXPORTING*" VALUE(EV_RESULT) TYPE P*" RAISING*" ZCX_DIVISION_BY_ZERO*"----------------------------------------------------------------------
IF iv_divisor = 0. RAISE EXCEPTION TYPE zcx_division_by_zero. ENDIF.
ev_result = iv_dividend / iv_divisor.
ENDFUNCTION.
" Aufruf mit TRY-CATCHTRY. CALL FUNCTION 'Z_DIVIDE_NUMBERS' EXPORTING iv_dividend = 100 iv_divisor = 0 IMPORTING ev_result = lv_result.
CATCH zcx_division_by_zero INTO DATA(lx_error). WRITE: / lx_error->get_text( ).ENDTRY.8. Funktionsgruppe mit globalen Daten
" TOP-Include der Funktionsgruppe (LZFGRP_CUSTOMERTOP)FUNCTION-POOL zfgrp_customer.
DATA: gt_cache TYPE HASHED TABLE OF kna1 WITH UNIQUE KEY kunnr.
" Function Module nutzt globale DatenFUNCTION z_customer_get_cached.*"----------------------------------------------------------------------*" IMPORTING*" VALUE(IV_KUNNR) TYPE KUNNR*" EXPORTING*" VALUE(ES_CUSTOMER) TYPE KNA1*"----------------------------------------------------------------------
" Erst im Cache suchen READ TABLE gt_cache INTO es_customer WITH KEY kunnr = iv_kunnr.
IF sy-subrc <> 0. " Aus DB laden und cachen SELECT SINGLE * FROM kna1 WHERE kunnr = @iv_kunnr INTO @es_customer.
IF sy-subrc = 0. INSERT es_customer INTO TABLE gt_cache. ENDIF. ENDIF.
ENDFUNCTION.
" Cache leerenFUNCTION z_customer_clear_cache. CLEAR gt_cache.ENDFUNCTION.9. Dynamischer Function Module Aufruf
DATA: lv_funcname TYPE funcname VALUE 'Z_CUSTOMER_GET_BY_ID', lt_params TYPE abap_func_parmbind_tab, lt_excps TYPE abap_func_excpbind_tab, ls_customer TYPE kna1, lv_kunnr TYPE kunnr VALUE '0000001000'.
" Parameter aufbauenlt_params = VALUE #( ( name = 'IV_KUNNR' kind = abap_func_exporting value = REF #( lv_kunnr ) ) ( name = 'ES_CUSTOMER' kind = abap_func_importing value = REF #( ls_customer ) )).
lt_excps = VALUE #( ( name = 'CUSTOMER_NOT_FOUND' value = 1 ) ( name = 'OTHERS' value = 99 )).
" Dynamischer AufrufCALL FUNCTION lv_funcname PARAMETER-TABLE lt_params EXCEPTION-TABLE lt_excps.
IF sy-subrc = 0. WRITE: / ls_customer-name1.ENDIF.10. Function Module testen (SE37)
" In SE37:" 1. Funktionsbaustein öffnen" 2. Menü: Funktionsbaustein -> Testen -> Einzeltest" 3. Parameter eingeben" 4. F8 zum Ausführen
" Oder programmatisch mit Unit TestCLASS ltcl_function_test DEFINITION FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION. METHODS: test_customer_get FOR TESTING.ENDCLASS.
CLASS ltcl_function_test IMPLEMENTATION. METHOD test_customer_get. DATA: ls_customer TYPE kna1, lv_found TYPE abap_bool.
CALL FUNCTION 'Z_CUSTOMER_GET_BY_ID' EXPORTING iv_kunnr = '0000001000' IMPORTING es_customer = ls_customer ev_found = lv_found EXCEPTIONS OTHERS = 1.
cl_abap_unit_assert=>assert_equals( exp = 0 act = sy-subrc msg = 'Function should succeed' ).
cl_abap_unit_assert=>assert_true( act = lv_found msg = 'Customer should be found' ). ENDMETHOD.ENDCLASS.11. Function Module Dokumentation
" In SE37:" Menü: Springen -> Dokumentation
" Strukturierte Dokumentation:" - Kurzbeschreibung" - Parameter-Dokumentation" - Beispielaufruf" - Exceptions erklärt
" Dokumentation im Code (für Entwickler)FUNCTION z_well_documented.*"----------------------------------------------------------------------*" Beschreibung:*" Berechnet den Gesamtpreis inkl. Mehrwertsteuer*"*" Parameter:*" IV_AMOUNT - Nettobetrag*" IV_TAX_RATE - Steuersatz in Prozent (z.B. 19)*" EV_TOTAL - Bruttobetrag*"*" Exceptions:*" INVALID_AMOUNT - Betrag ist negativ*" INVALID_TAX_RATE - Steuersatz außerhalb 0-100*"*" Beispiel:*" CALL FUNCTION 'Z_WELL_DOCUMENTED'*" EXPORTING iv_amount = 100 iv_tax_rate = 19*" IMPORTING ev_total = lv_total.*" " lv_total = 119.00*"---------------------------------------------------------------------- " Implementation...ENDFUNCTION.12. Conversion Exits
" Spezielle Function Modules für Formatierung" Namenskonvention: CONVERSION_EXIT_<NAME>_INPUT/OUTPUT
FUNCTION conversion_exit_alpha_input.*" Führende Nullen hinzufügen*" IMPORTING VALUE(INPUT)*" EXPORTING VALUE(OUTPUT)
CALL FUNCTION 'CONVERSION_EXIT_ALPHA_INPUT' EXPORTING input = '1000' IMPORTING output = lv_kunnr. " lv_kunnr = '0000001000'
ENDFUNCTION.
FUNCTION conversion_exit_alpha_output.*" Führende Nullen entfernen
CALL FUNCTION 'CONVERSION_EXIT_ALPHA_OUTPUT' EXPORTING input = '0000001000' IMPORTING output = lv_display. " lv_display = '1000'
ENDFUNCTION.13. Popup Function Modules
" Ja/Nein PopupDATA: lv_answer TYPE c LENGTH 1.
CALL FUNCTION 'POPUP_TO_CONFIRM' EXPORTING titlebar = 'Bestätigung' text_question = 'Wollen Sie fortfahren?' text_button_1 = 'Ja' text_button_2 = 'Nein' default_button = '2' display_cancel_button = abap_false IMPORTING answer = lv_answer.
IF lv_answer = '1'. " Ja gewähltENDIF.
" TexteingabeDATA: lv_input TYPE string.
CALL FUNCTION 'POPUP_GET_VALUES' EXPORTING popup_title = 'Eingabe' TABLES fields = lt_fields.
" Meldung anzeigenCALL FUNCTION 'POPUP_TO_DISPLAY_TEXT' EXPORTING titel = 'Information' textline1 = 'Verarbeitung abgeschlossen.' textline2 = '100 Datensätze verarbeitet.'.14. Hintergrundverarbeitung
" Function Module für Background-JobFUNCTION z_batch_process.*"----------------------------------------------------------------------*" IMPORTING*" VALUE(IV_DATE) TYPE SY-DATUM*"----------------------------------------------------------------------
" Kann in Background-Job ausgeführt werden " Keine Dialogaufrufe!
SELECT * FROM vbak WHERE erdat = @iv_date INTO TABLE @DATA(lt_orders).
LOOP AT lt_orders INTO DATA(ls_order). " Verarbeitung ohne Benutzerinteraktion ENDLOOP.
ENDFUNCTION.
" Als Job einplanenSUBMIT zreport_calling_function VIA JOB lv_jobname NUMBER lv_jobcount AND RETURN.15. Wrapper-Klasse für Function Modules
CLASS zcl_function_wrapper DEFINITION. PUBLIC SECTION. " Typsichere Wrapper für häufig verwendete FuBas
CLASS-METHODS: convert_to_internal IMPORTING iv_external TYPE clike RETURNING VALUE(rv_internal) TYPE kunnr.
CLASS-METHODS: convert_to_external IMPORTING iv_internal TYPE kunnr RETURNING VALUE(rv_external) TYPE string.
CLASS-METHODS: calculate_date IMPORTING iv_date TYPE sy-datum iv_days TYPE i DEFAULT 0 iv_months TYPE i DEFAULT 0 iv_years TYPE i DEFAULT 0 RETURNING VALUE(rv_date) TYPE sy-datum.ENDCLASS.
CLASS zcl_function_wrapper IMPLEMENTATION. METHOD convert_to_internal. CALL FUNCTION 'CONVERSION_EXIT_ALPHA_INPUT' EXPORTING input = iv_external IMPORTING output = rv_internal. ENDMETHOD.
METHOD convert_to_external. CALL FUNCTION 'CONVERSION_EXIT_ALPHA_OUTPUT' EXPORTING input = iv_internal IMPORTING output = rv_external. ENDMETHOD.
METHOD calculate_date. DATA: lv_sign TYPE c LENGTH 1.
" Positiv oder negativ IF iv_days >= 0 AND iv_months >= 0 AND iv_years >= 0. lv_sign = '+'. ELSE. lv_sign = '-'. ENDIF.
CALL FUNCTION 'RP_CALC_DATE_IN_INTERVAL' EXPORTING date = iv_date days = abs( iv_days ) months = abs( iv_months ) signum = lv_sign years = abs( iv_years ) IMPORTING calc_date = rv_date. ENDMETHOD.ENDCLASS.
" VerwendungDATA(lv_kunnr) = zcl_function_wrapper=>convert_to_internal( '1000' ).DATA(lv_next_month) = zcl_function_wrapper=>calculate_date( iv_date = sy-datum iv_months = 1).Wichtige Transaktionen
| Transaktion | Beschreibung |
|---|---|
| SE37 | Function Builder |
| SE80 | Repository Browser |
| SM59 | RFC-Destinationen |
Wichtige Hinweise / Best Practice
- Funktionsgruppen logisch nach Thema organisieren.
- VALUE() für IMPORTING-Parameter (by Value).
- RFC-fähig nur wenn wirklich nötig (Performance).
- TABLES vermeiden – stattdessen TYPE für Tabellen.
- Exceptions für alle Fehlerfälle definieren.
- Dokumentation in SE37 pflegen.
- Globale Daten in Funktionsgruppe für Caching nutzen.
- Klassenbasierte Exceptions für OO-Integration.
- Keine Dialoge in RFC-fähigen Bausteinen.
- Kombinieren Sie mit BAPI Development für APIs.