[ABAP] Übersicht SAP-Programmerweiterungen

Übersicht

Typ                         Objekt                Transaktion       Bemerkung

User-Exit                   Unterprogramm (FORM)  SPDD, SPAM, SPAU  Modifikation
Customer-Exit               Funktionsbaustein     SMOD, CMOD        Erweiterung
Business Transaction Event  Funktionsbaustein                       Erweiterung
Business Add In (BAdI)      Interface, Klasse     SE18, SE19        Erweiterung
Enhancement Points          Interface, Klasse     SE18, SE19        Erweiterung, Verwaltung durch Enhancement Framework (+neue und alte BAdIs)

User-Exit

PERFORM userexit_<name>.

* z.B. Include MV45AFZB (vom SAP-Entwickler bereitgestellt)
FORM userexit_<name>.
ENDFORM.

Customer-Exit

Infos zu Customer-Exits: Link

CALL CUSTOMER-FUNCTION '001'
  EXPORTING
    ...
  IMPORTING
    ...

* Aufruf Include mit Quelltext
FUNCTION-POOL XYZ.
  FUNCTION exit_<programm_name>_001.
    ...
    INCLUDE zxaaau01.
    ...
  ENDFUNCTION.

BAdI

Infos zu BAdIs: Link
Infos zu BADI_SORTER: Link

* Objektreferenz für Methoden des BAdI-Interfaces
cl_exithandler=>get_instance( )
* Methoden der BAdI-Erweiterung aufrufen
o_badi->method_abc( )

* Tabellen
* SXS_ATTR (Feld EXIT_NAME, ACTIVE = 'X')
* SXC_ATTR (Feld EXIT_NAME)
* SXS_ATTRT (Feld EXIT_NAME)
* SXC_EXIT (Feld EXIT_NAME, IMP_NAME)

* Abarbeitungsrreihenfolge über Implementierung von BADI_SORTER erzwingen

Enhancement-Points

Infos zu Enhancement-Points: Link

* keine Import/Export-Schnittstelle
* implizit: automatisch vorhandene Absprungpunkte (z.B. Anfang/Ende des Quellcodes)
*           im Menu einblendbar
* explizit: von SAP vorbereitete Absprungpunkte
ENHANCEMENT-POINT EP_A SPOTS ESPOT_X.
  ...

ENHANCEMENT-SECTION ES_B SPOTS ESPOT_X.
  ...
END-ENHANCEMENT-SECTION.

[ABAP] Code von Klassen und Interfaces über Eclipse-Klasse (cl_oo_factory) lesen

TRY.
* Objektname: Klasse oder Interface
    DATA(lv_obj_name) = |CL_DEMO_OUTPUT|.

    DATA(o_source) = cl_oo_factory=>create_instance( )->create_clif_source( clif_name = lv_obj_name
                                                                            version   = if_oo_clif_source=>co_version_active ).

* Typ ausgeben
    WRITE: / COND string( WHEN o_source->get_type( ) = 0 THEN 'Class' ELSE 'Interface' ).

    ULINE.

* Code zum Objekt holen
    DATA(it_code) = VALUE rswsourcet( ).
    o_source->get_source( IMPORTING source = it_code ).

* Code ausgeben
    LOOP AT it_code ASSIGNING FIELD-SYMBOL(<c>).
      WRITE: / <c>.
    ENDLOOP.

  CATCH cx_root INTO DATA(e_txt).
    WRITE: / e_txt->get_text( ).
ENDTRY.

[ABAP] Pragmas im Quellcode verwenden

Weiterführende Infos: Link und Link

* Ausblenden von Syntaxwarnungen der normalen Syntaxprüfung oder der erweiterten Syntaxprüfung
* lösen die bisherhigen Pseudokommentare der erweiterten Syntaxprüfung ab
* Pragmas können Parameter haben, z.B.: ##SHADOW[SUBSTRING]

DATA: t TYPE string ##needed.
t = 'Testtext'      ##no_text.
WRITE: / t.

* oft benutzte Pragmas:

* ##called      - FORM oder METHOD nicht aufgerufen
* ##catch_all   - Ausnahmebehandlung mit CX_ROOT
* ##fm_subrc_ok - Zugriff auf undefinierten Returncode nach CALL FUNCTION
* ##needed      - unbenutzte Datendeklaration
* ##no_text     - hardcodierte Texte
* ##no_handler  - Ausnahmebehandlung ist leer

* Programm: ABAP_SLIN_PRAGMAS

[ABAP] VBA-Code ausführen

* VBA-Code
DATA(it_vba_code) = VALUE stringtab( ( |Dim app| )
                                     ( |Set app = CreateObject("Access.Application")| )
                                     ( |Dim msg| )
                                     ( |msg = MsgBox ("My Message.", 0, "Warning")| ) ).

DATA: lv_temp_dir TYPE string.

* Temp-Directory holen
cl_gui_frontend_services=>get_desktop_directory( CHANGING desktop_directory = lv_temp_dir ).
* Note 1442303, sonst ist lv_temp_dir leer
cl_gui_cfw=>flush( ).

* Dateinamen zusammenbauen
DATA(lv_filename) = |{ lv_temp_dir }\\my_script.vbs|.

* Datei im Zielverzeichnis erzeugen
cl_gui_frontend_services=>gui_download( EXPORTING
                                          filename = lv_filename
                                          filetype = 'ASC'
                                        CHANGING
                                          data_tab = it_vba_code ).

LOOP AT it_vba_code ASSIGNING FIELD-SYMBOL(<fs_line>).
  WRITE: / <fs_line>.
ENDLOOP.

SKIP.

WRITE: / lv_filename.

* VBA über Scripting Host ausführen
* "" für lv_filename ergänzen, sonst wird der Pfad unter parameter nicht korrekt übergeben,
* da Leerzeichen im Pfad als Trennung in einzelne Parameter erkannt wird
cl_gui_frontend_services=>execute( application = 'WSCRIPT.EXE'
                                   parameter   = |"{ lv_filename }"| ).

[ABAP] Auf Variablen eines anderen (übergeordneten) Reports zugreifen (Dirty Assign)

* Sinngemäße Übersetzung des Kommentars von Thomas Jung auf Seite: http://www.dataxstream.com/2009/08/use-abap-to-access-any-data-in-memory/
* "Diese Syntax-Variante ("Dirty-Assign") ist nur für den internen Gebrauch. SAP supportet diese Syntax-Variante nicht
*  und kann jederzeit in zukünftigen Releases das Statement entfernen oder die Funktionsweise ändern.
*  Ursprünglich wurde dieses Konzept vom ABAP-Debugger verwendet. Der klassische Debugger lief in der selben
*  Session wie das zu debuggende Programm. Auf diese Art und Weise konnte der Debuggger auf die Speicherwerte zugreifen.
*  Ab Release 6.40 läuft der neue ABAP-Debugger in einer separaten Session.
*  Der ABAP-Debugger greift nun remote in die laufende Debug-Session  und benutzt dazu spezielle Kernel-Methoden.
*  Irgendwann entfernt SAP den klassichen Debugger und damit auch diese Syntax-Variante."

* beliebiger übergeordneter Source-Report Name
DATA: lv_report_name TYPE string VALUE 'ZDEMO1'.

* Variablen
DATA: lv_int TYPE i VALUE 1.
DATA: it_spfli TYPE STANDARD TABLE OF spfli WITH DEFAULT KEY.

* Demo-Daten in iTab schreiben
SELECT * from spfli INTO TABLE @it_spfli.

* int-Variable aus Report holen
DATA(lv_var1) = |({ lv_report_name })LV_INT|.

* Feldsymbol zuweisen
ASSIGN (lv_var1) TO FIELD-SYMBOL(<fs_lv_int>).
* Wert ausgeben
IF <fs_lv_int> IS ASSIGNED.
  WRITE: / <fs_lv_int>.
ENDIF.

* iTab aus Report holen
DATA(lv_var2) = |({ lv_report_name })IT_SPFLI|.
* Feldsymbol für iTab kann nicht inline deklariert werden,
* da Typ noch unbekannt und deshalb Fehler bei lines( )
FIELD-SYMBOLS: <fs_it_spfli> TYPE ANY TABLE.

* Feldsymbol zuweisen
ASSIGN (lv_var2) TO <fs_it_spfli>.
* Werte ausgeben
IF <fs_it_spfli> IS ASSIGNED.
  WRITE: / lines( <fs_it_spfli> ).
ENDIF.

Links

[ABAP] Objekt vom Code aus aktivieren

PARAMETERS: p_prg  TYPE e071-obj_name.
PARAMETERS: p_type TYPE sewor_working_area-object DEFAULT 'REPS'. " REPS -> report, METH -> method

CALL FUNCTION 'REPS_OBJECT_ACTIVATE'
  EXPORTING
    object_name  = p_prg
    object_type  = p_type
  EXCEPTIONS
    not_executed = 1
    OTHERS       = 2.

IF sy-subrc NE 0.
  WRITE: / 'Fehler beim aktivieren des Objektes:', p_prg.
ENDIF.

[ABAP] Dynamisch Programmcode erzeugen und persistent auf der Datenbank speichern

* Programmname
DATA: lv_prg_name TYPE sy-repid VALUE 'ZREPORT1'.
* Compilerausgaben
DATA: lv_msg TYPE string.
DATA: lv_line TYPE string.
DATA: lv_word TYPE string.
DATA: lv_off TYPE string.
* iTab mit Quellcode
DATA: it_src TYPE STANDARD TABLE OF char1024.

* Codezeilen zu iTab hinzufügen
APPEND |REPORT { lv_prg_name }.| TO it_src.
APPEND |WRITE: / 'Hallo Welt!'.| TO it_src.

* ausführbares Programm (TYPE '1') anlegen, es wird persistent auf der Datenbank gespeichert,
* aber keinem Paket zugeordnet und ist global verfügbar
INSERT REPORT lv_prg_name FROM it_src PROGRAM TYPE '1' UNICODE ENABLING 'X'.

* Programmcode compilieren, existierender Programmcode wird überschrieben
GENERATE REPORT lv_prg_name MESSAGE lv_msg LINE lv_line WORD lv_word OFFSET lv_off.

IF sy-subrc = 0.
* wenn Compilierung ok: Code ausführen
  SUBMIT (lv_prg_name) AND RETURN.
ELSE.
* Im Fehlerfall: Fehlerausgabe
  WRITE: / 'Error during generation in line: ', lv_line.
  IF lines( it_src ) > 0.
    WRITE: it_src[ lv_line ].
  ENDIF.
  WRITE: / lv_msg.
  WRITE: / 'Word: ', lv_word.
  WRITE: / 'Offset: ', lv_off.
ENDIF.

* aktives Programm wieder aus der DB löschen
DELETE REPORT lv_prg_name.

CASE sy-subrc.
  WHEN 0.
    WRITE: / |{ lv_prg_name } gelöscht.|.
  WHEN 4.
    WRITE: / |{ lv_prg_name } nicht vorhanden.|.
  WHEN OTHERS.
    WRITE: / |Anderer Fehler.|.
ENDCASE.

Weiterführende Infos: Link

[ABAP] ABAP Code-Editor entsperren

PARAMETER: p_name TYPE trdir-name.

START-OF-SELECTION.

  DATA: lv_trdir TYPE trdir.

  SELECT SINGLE * FROM trdir INTO @lv_trdir WHERE name = @p_name.

  IF sy-subrc = 0.
    IF lv_trdir-edtx = 'X'.
      lv_trdir-edtx = space.

      CALL FUNCTION 'RS_TRDIR_UPDATE'
        EXPORTING
          trdir_row       = lv_trdir
        EXCEPTIONS
          internal_error  = 1
          parameter_error = 2
          not_found       = 3
          OTHERS          = 4.

      IF sy-subrc EQ 0.
        WRITE:/ 'Editor entsperrt.'.
      ENDIF.

    ELSE.
      WRITE:/ 'Editor ist schon entsperrt.'.
    ENDIF.
  ENDIF.

[ABAP] Breakpoints eines Programmes zur Laufzeit ermitteln

DATA: it_breakpoints TYPE STANDARD TABLE OF breakpoint.

CALL FUNCTION 'SYSTEM_DEBUG_BREAKPOINTS'
  EXPORTING
    main_program     = sy-repid
  TABLES
    breakpoints      = it_breakpoints
  EXCEPTIONS
    c_call_error     = 1
    wrong_parameters = 2
    OTHERS           = 3.

IF sy-subrc = 0.
  LOOP AT it_breakpoints ASSIGNING FIELD-SYMBOL(<fs_line>).
    WRITE: / <fs_line>-program, <fs_line>-line.
  ENDLOOP.
ENDIF.