Les Function Modules (modules de fonction) sont des routines ABAP reutilisables avec une interface definie. Ils sont organises en groupes de fonctions et peuvent etre compatibles RFC pour les appels distants.
Concept de base
| Element | Description |
|---|---|
| Groupe de fonctions | Conteneur pour les Function Modules |
| IMPORTING | Parametres d’entree |
| EXPORTING | Parametres de sortie |
| CHANGING | Parametres d’entree/sortie |
| TABLES | Parametres de table (obsolete) |
| EXCEPTIONS | Gestion des erreurs |
Syntaxe
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. " Gestion des erreursENDIF.Exemples
1. Appeler un Function Module
DATA: lv_date_out TYPE sy-datum.
" Calculer une dateCALL FUNCTION 'RP_CALC_DATE_IN_INTERVAL" EXPORTING date = sy-datum days = 30 months = 0 signum = '+" years = 0 IMPORTING calc_date = lv_date_out.
WRITE: / 'Date + 30 jours:', lv_date_out.2. Function Module avec 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: / 'Resultat:', lv_result. WHEN 1. WRITE: / 'Entree invalide'. WHEN OTHERS. WRITE: / 'Erreur inconnue'.ENDCASE.3. Creer son propre Function Module
" Dans SE37 ou SE80:" 1. Creer un groupe de fonctions : ZFGRP_CUSTOMER" 2. Creer un module de fonction : Z_CUSTOMER_GET_BY_ID
FUNCTION z_customer_get_by_id.*"----------------------------------------------------------------------*"*"Interface locale:*" IMPORTING*" VALUE(IV_KUNNR) TYPE KUNNR*" EXPORTING*" VALUE(ES_CUSTOMER) TYPE KNA1*" VALUE(EV_FOUND) TYPE ABAP_BOOL*" EXCEPTIONS*" CUSTOMER_NOT_FOUND*" INVALID_INPUT*"----------------------------------------------------------------------
" Verification de l'entree IF iv_kunnr IS INITIAL. RAISE invalid_input. ENDIF.
" Lire le client 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.
" AppelDATA: 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. Function Module compatible RFC
" Parametre dans SE37:" Attributes -> Processing Type: Remote-Enabled Module
FUNCTION z_rfc_get_sales_data.*"----------------------------------------------------------------------*"*"Interface locale:*" 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*"----------------------------------------------------------------------
" Verifier la plage de dates IF iv_date_from > iv_date_to. RAISE invalid_date_range. ENDIF.
" Lire les donnees 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.
" Appel distantCALL FUNCTION 'Z_RFC_GET_SALES_DATA" DESTINATION 'PRD_100' " Destination RFC 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: / 'Erreur RFC:', lv_msg.ENDIF.5. Function Module avec parametre TABLES
" TABLES est obsolete mais encore souvent utiliseFUNCTION z_process_items.*"----------------------------------------------------------------------*"*"Interface locale:*" TABLES*" IT_ITEMS STRUCTURE ZITEM*" ET_RESULTS STRUCTURE ZRESULT*"----------------------------------------------------------------------
LOOP AT it_items. " Traitement et_results-id = it_items-id. et_results-status = 'OK'. APPEND et_results. ENDLOOP.
ENDFUNCTION.
" Alternative moderne : TYPE pour les parametres de tableFUNCTION 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
" Pour les modifications de base de donnees asynchronesFUNCTION z_update_customer IN UPDATE TASK.*"----------------------------------------------------------------------*" IMPORTING*" VALUE(IS_CUSTOMER) TYPE KNA1*"----------------------------------------------------------------------
MODIFY kna1 FROM is_customer.
ENDFUNCTION.
" AppelCALL FUNCTION 'Z_UPDATE_CUSTOMER' IN UPDATE TASK EXPORTING is_customer = ls_customer.
" Le commit execute la mise a jourCOMMIT WORK AND WAIT.7. Function Module avec exceptions basees sur des classes
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.
" Appel avec 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. Groupe de fonctions avec donnees globales
" Include TOP du groupe de fonctions (LZFGRP_CUSTOMERTOP)FUNCTION-POOL zfgrp_customer.
DATA: gt_cache TYPE HASHED TABLE OF kna1 WITH UNIQUE KEY kunnr.
" Le Function Module utilise les donnees globalesFUNCTION z_customer_get_cached.*"----------------------------------------------------------------------*" IMPORTING*" VALUE(IV_KUNNR) TYPE KUNNR*" EXPORTING*" VALUE(ES_CUSTOMER) TYPE KNA1*"----------------------------------------------------------------------
" D'abord chercher dans le cache READ TABLE gt_cache INTO es_customer WITH KEY kunnr = iv_kunnr.
IF sy-subrc <> 0. " Charger depuis la DB et mettre en cache 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.
" Vider le cacheFUNCTION z_customer_clear_cache. CLEAR gt_cache.ENDFUNCTION.9. Appel dynamique de Function Module
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'.
" Construire les parametreslt_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 )).
" Appel dynamiqueCALL FUNCTION lv_funcname PARAMETER-TABLE lt_params EXCEPTION-TABLE lt_excps.
IF sy-subrc = 0. WRITE: / ls_customer-name1.ENDIF.10. Tester un Function Module (SE37)
" Dans SE37:" 1. Ouvrir le module de fonction" 2. Menu: Module de fonction -> Tester -> Test individuel" 3. Saisir les parametres" 4. F8 pour executer
" Ou par programmation avec 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 = 'La fonction devrait reussir" ).
cl_abap_unit_assert=>assert_true( act = lv_found msg = 'Le client devrait etre trouve" ). ENDMETHOD.ENDCLASS.11. Documentation des Function Modules
" Dans SE37:" Menu: Saut -> Documentation
" Documentation structuree:" - Description courte" - Documentation des parametres" - Exemple d'appel" - Explications des exceptions
" Documentation dans le code (pour les developpeurs)FUNCTION z_well_documented.*"----------------------------------------------------------------------*" Description:*" Calcule le prix total TTC*"*" Parametres:*" IV_AMOUNT - Montant net*" IV_TAX_RATE - Taux de taxe en pourcentage (ex. 19)*" EV_TOTAL - Montant brut*"*" Exceptions:*" INVALID_AMOUNT - Le montant est negatif*" INVALID_TAX_RATE - Le taux de taxe est hors de 0-100*"*" Exemple:*" 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
" Function Modules speciaux pour le formatage" Convention de nommage: CONVERSION_EXIT_<NAME>_INPUT/OUTPUT
FUNCTION conversion_exit_alpha_input.*" Ajouter des zeros en tete*" 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.*" Supprimer les zeros en tete
CALL FUNCTION 'CONVERSION_EXIT_ALPHA_OUTPUT" EXPORTING input = '0000001000" IMPORTING output = lv_display. " lv_display = '1000"
ENDFUNCTION.13. Popup Function Modules
" Popup Oui/NonDATA: lv_answer TYPE c LENGTH 1.
CALL FUNCTION 'POPUP_TO_CONFIRM" EXPORTING titlebar = 'Confirmation" text_question = 'Voulez-vous continuer ?" text_button_1 = 'Oui" text_button_2 = 'Non" default_button = '2" display_cancel_button = abap_false IMPORTING answer = lv_answer.
IF lv_answer = '1'. " Oui choisiENDIF.
" Saisie de texteDATA: lv_input TYPE string.
CALL FUNCTION 'POPUP_GET_VALUES" EXPORTING popup_title = 'Saisie" TABLES fields = lt_fields.
" Afficher un messageCALL FUNCTION 'POPUP_TO_DISPLAY_TEXT" EXPORTING titel = 'Information" textline1 = 'Traitement termine." textline2 = '100 enregistrements traites.'.14. Traitement en arriere-plan
" Function Module pour job d'arriere-planFUNCTION z_batch_process.*"----------------------------------------------------------------------*" IMPORTING*" VALUE(IV_DATE) TYPE SY-DATUM*"----------------------------------------------------------------------
" Peut etre execute en job d'arriere-plan " Pas d'appels de dialogue !
SELECT * FROM vbak WHERE erdat = @iv_date INTO TABLE @DATA(lt_orders).
LOOP AT lt_orders INTO DATA(ls_order). " Traitement sans interaction utilisateur ENDLOOP.
ENDFUNCTION.
" Planifier comme jobSUBMIT zreport_calling_function VIA JOB lv_jobname NUMBER lv_jobcount AND RETURN.15. Classe wrapper pour Function Modules
CLASS zcl_function_wrapper DEFINITION. PUBLIC SECTION. " Wrappers type-safe pour les FuBas frequemment utilises
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.
" Positif ou negatif 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.
" UtilisationDATA(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).Transactions importantes
| Transaction | Description |
|---|---|
| SE37 | Function Builder |
| SE80 | Repository Browser |
| SM59 | Destinations RFC |
Conseils importants / Bonnes pratiques
- Organiser les groupes de fonctions logiquement par theme.
- VALUE() pour les parametres IMPORTING (par valeur).
- Compatible RFC uniquement si vraiment necessaire (performance).
- Eviter TABLES - utiliser TYPE pour les tables.
- Definir des exceptions pour tous les cas d’erreur.
- Maintenir la documentation dans SE37.
- Utiliser les donnees globales dans le groupe de fonctions pour le cache.
- Exceptions basees sur des classes pour l’integration OO.
- Pas de dialogues dans les modules compatibles RFC.
- Combiner avec BAPI Development pour les APIs.