ABAP Function Modules: Funktionsbausteine erstellen und nutzen

kategorie
ABAP-Statements
Veröffentlicht
autor
Johannes

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

ElementBeschreibung
FunktionsgruppeContainer für Function Modules
IMPORTINGEingabeparameter
EXPORTINGAusgabeparameter
CHANGINGEin-/Ausgabeparameter
TABLESTabellenparameter (veraltet)
EXCEPTIONSFehlerbehandlung

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.
" Fehlerbehandlung
ENDIF.

Beispiele

1. Function Module aufrufen

DATA: lv_date_out TYPE sy-datum.
" Datum berechnen
CALL 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.
" Aufruf
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
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-Aufruf
CALL 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 verwendet
FUNCTION 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 Tabellenparameter
FUNCTION 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änderungen
FUNCTION z_update_customer IN UPDATE TASK.
*"----------------------------------------------------------------------
*" IMPORTING
*" VALUE(IS_CUSTOMER) TYPE KNA1
*"----------------------------------------------------------------------
MODIFY kna1 FROM is_customer.
ENDFUNCTION.
" Aufruf
CALL FUNCTION 'Z_UPDATE_CUSTOMER' IN UPDATE TASK
EXPORTING
is_customer = ls_customer.
" Commit führt Update aus
COMMIT 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-CATCH
TRY.
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 Daten
FUNCTION 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 leeren
FUNCTION 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 aufbauen
lt_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 Aufruf
CALL 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 Test
CLASS 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 Popup
DATA: 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ählt
ENDIF.
" Texteingabe
DATA: lv_input TYPE string.
CALL FUNCTION 'POPUP_GET_VALUES'
EXPORTING
popup_title = 'Eingabe'
TABLES
fields = lt_fields.
" Meldung anzeigen
CALL FUNCTION 'POPUP_TO_DISPLAY_TEXT'
EXPORTING
titel = 'Information'
textline1 = 'Verarbeitung abgeschlossen.'
textline2 = '100 Datensätze verarbeitet.'.

14. Hintergrundverarbeitung

" Function Module für Background-Job
FUNCTION 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 einplanen
SUBMIT 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.
" Verwendung
DATA(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

TransaktionBeschreibung
SE37Function Builder
SE80Repository Browser
SM59RFC-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.