Sebastian Wey,

FS-CD

Das Vertragskontokorrent FI-CA, auf dem FS-CD als branchenspezifische Erweiterung für Versicherungen basiert, stellt für branchen- und kundenspezifische Erweiterungen ein zweistufiges Erweiterungskonzept bereit. Dieses Konzept basiert auf Zeitpunkten, die von FI-CA bzw. FS-CD ausgelöst werden. Derzeit sind 1.686 Zeitpunkte definiert, davon 1.365 von FI-CA und 321 von FS-CD (ERP 6.05 SP 8).

Für jeden Zeitpunkt können Funktionsbausteine im Customizing eingetragen werden (Transaktion FQEVENTS), wobei die Schnittstelle der Bausteine von SAP vorgegeben wird. Weiterhin wird von SAP festgelegt, ob ein Zeitpunkt nur einfach oder ob er mehrfach implementierbar ist. Diese Unterscheidung lässt sich anhand der Charakteristik des Zeitpunkts erklären. Hat der Zeitpunkt einen Ereignis-Charakter, d. h. das auslösende Programm möchte auf eine bestimmte Situation aufmerksam machen, ohne ein bestimmtes Ergebnis zu erwarten, ist der Zeitpunkt mehrfach implementierbar. Dies entspricht dem Observer-Pattern. Benötigt der Aufrufer hingegen ein Ergebnis aus der Erweiterung, ist der Zeitpunkt nur einfach implementierbar. Dies entspricht dem Template-Method-Pattern.

Durch FI-CA werden Muster-Bausteine bereitgestellt. Weiterhin stellt FS-CD branchenspezifische Bausteine bereit. Für bestimmte Zeitpunkte kann FS-CD ausschließen, dass kundenspezifischen Bausteine zulässig sind. Bei einfach implementierbaren Zeitpunkten wird der erste der folgenden Bausteine – sofern vorhanden – aufgerufen: Kundenbaustein, FS-CD-Baustein, Musterbaustein. Bei mehrfacher Implementierbarkeit werden zunächst alle FS-CD-Bausteine und danach alle Kundenbausteine prozessiert. Der Musterbaustein ist in diesem Fall leer und dient lediglich dazu, die Schnittstelle zu definieren.

Was passiert aber, wenn die Anforderungen mehrerer Fachbereiche im gleichen System und im gleichen Mandanten abgebildet werden sollen? Bei mehrfach implementierbaren Zeitpunkten könnten zwar alle Fachbereiche ihre Zusatzfunktionalität hinterlegen, es liegt aber in der Verantwortung des jeweiligen Entwicklers, dass die Zusatzfunktionalität seines Fachbereichs nicht für andere Fachbereiche ausgeführt wird. Bei einfach implementierbaren Zeitpunkten müssten sich mehrere Fachbereiche einen Funktionsbaustein teilen. Folgende Probleme können auftreten:

Die einfachste Lösung wäre natürlich getrennte Mandanten, aber dies ist den meisten Fällen nicht realistisch. Aus diesem Grund möchte ich ein Konzept zur Lösung dieses Problems vorstellen, das ich vor kurzem bei einem Kunden implementieren konnte. Es handelt sich dabei um ein Framework, das die Vermittlung zwischen dem Zeitpunkt und der fachbereichsspezifischen Funktionalität übernimmt. Dieses Framework, ich habe es "FS-CD Business Domain Filter" genannt, soll dazu in einem ersten Schritt aus den verfügbaren Informationen, die im jeweiligen Zeitpunkt verfügbar sind, den aktuellen Fachbereich ermitteln. In einem zweiten Schritt übernimmt das Framework den Aufruf der fachbereichsspezifischen Zusatzfunktionalität. Das Framework benötigt folgende Komponenten:

Ableitung des Fachbereichs

Im DDIC werden ein Datenelement und eine Festwertdomäne ZCD_BDOMAIN angelegt. Der Datentyp ist dabei beliebig, also z. B. CHAR 3. Die Festwerte entsprechen den Fachbereichen. In jedem Zeitpunkt stehen unterschiedliche Informationen bereit, anhand derer der Fachbereich abgeleitet werden kann. Ich werde hier als Beispiel den Vertragskontotyp verwenden, der in vielen Zeitpunkten verfügbar ist. Andere Kandidaten wären bspw. der Buchungskreis, die Produktgruppe oder der Geschäftsbereich. Teilweise können auch komplexere Ableitungen erforderlich sein, z. B. von der Vertragskontonummer zum Vertragskontotyp zum Fachbereich.

Für alle Ableitungsmethoden wird im Interface ZIF_CD_BDOMAIN_DERIVE je eine Methode definiert, die dann in der Klasse ZCL_CD_BDOMAIN_DERIVE implementiert werden. Es ist grundsätzlich sinnvoll, Interfaces zu definieren, um eine lose Koppelung zwischen einer Klasse und ihren Verwendern zu erreichen. Die Abbildung von Vertragskontotyp zu Fachbereich wird in der neuen Customizing-Tabelle ZCD_VKTYP_BDOM abgelegt:

AttributKey?DatenelementTypLängeBeschreibung
CLIENTXMANDTCLNT3Mandant
VKTYPXVKTYP_KKCHAR2Vertragskontotyp
BDOMAINZCD_BDOMAINCHAR3Fachbereich

Zu dieser und allen weiteren Customizing-Tabellen sollte natürlich eine Pflegeview mit Pflegedialog erstellt werden, was ich hier aber nicht näher beschreiben werde.

Da viele Zeitpunkte auch in Batchprozessen aufgerufen werden, empfiehlt es sich aus Performance-Gründen, die Customizing-Tabelle im Konstruktor der Klasse vollständig zu laden. Wir definieren also das Instanzattribut GT_VKTYP_BDOM in der Klasse ZCL_CD_BDOMAIN_DERIVE mit direkter Typeingabe:
TYPE HASHED TABLE OF zcd_vktyp_bdom WITH UNIQUE KEY vktyp.

Die Definition eines entsprechenden Tabellentyps im DDIC oder in einem TYPE-POOL wäre natürlich auch möglich. Im Konstruktor werden dann die Tabelleneinträge vollständig geladen:
SELECT * FROM zcd_vktyp_bdom INTO TABLE gt_vktyp_bdom.

Die Ableitungsmethode sieht dann so aus:

ArtParameterTyp
IMPORTINGVALUE( IV_VKTYP )ZCD_VKTYP_BDOM-VKTYP
RETURNINGVALUE( RV_BDOMAIN )ZCD_VKTYP_BDOM-BDOMAIN
METHOD zif_cd_bdomain_derive~get_bdomain_by_vktyp.
  DATA ref_vktyp_bdom TYPE REF TO zcd_vktyp_bdom.
  READ TABLE gt_vktyp_bdom REFERENCE INTO ref_vktyp_bdom WITH TABLE KEY vktyp = iv_vktyp.
  IF sy-subrc EQ 0.
    MOVE ref_vktyp_bdom->bdomain TO rv_bdomain.
  ELSE.
    CLEAR rv_bdomain.
  ENDIF.
ENDMETHOD.

Ermittlung des aufzurufenden Funktionsbausteins

Für die Ermittlung des fachbereichsspezifischen Bausteins wird eine Customizing-Tabelle ZCD_BDOMAIN_FUNC benötigt, die folgenden Aufbau hat:

AttributKey?DatenelementTypLängeBeschreibung
CLIENTXMANDTCLNT3Mandant
BDOMAINXZCD_BDOMAINCHAR3Fachbereich
EVENTXFBEVE_KKCHAR4Zeitpunkt
FUNCTIONFUNCC_KKCHAR30Funktionsbaustein

Der gewählte Primärschlüssel der Customizing-Tabelle erlaubt es, für einen Fachbereich eine Änderung zu transportieren, ohne dass andere Fachbereiche betroffen sind. Wenn der Fachbereich nicht im Schlüssel wäre, hätte eine Änderung eines Eintrags u. U. Auswirkungen auf einen anderen Fachbereich.

Die Methode zum Ermitteln und Aufrufen des Funktionsbausteins wird unabhängig von der Logik zum Ermitteln des Fachbereichs in einer eigenen Klasse ZCL_CD_EVENT_INVOKER gekapselt, die das Interface ZIF_CD_EVENT_INVOKER implementiert.

ArtParameterTyp
IMPORTINGVALUE( IV_BDOMAIN )ZCD_BDOMAIN_FUNC-BDOMAIN
IMPORTINGVALUE( IV_EVENT )ZCD_BDOMAIN_FUNC-EVENT
IMPORTINGIT_PARMBINDABAP_FUNC_PARMBIND_TAB
IMPORTINGIT_EXCPBINDABAP_FUNC_EXCPBIND_TAB
RETURNINGVALUE( RV_SUBRC )SYSUBRC
METHOD zif_cd_event_invoker~invoke_event.
  DATA lv_function TYPE zcd_bdomain_func-function.
  lv_function = get_function_by_bdomain(
    iv_bdomain = iv_bdomain
    iv_event   = iv_event ).
  IF NOT lv_function IS INITIAL.
    CALL FUNCTION lv_function
      PARAMETER-TABLE
        it_parmbind
      EXCEPTION-TABLE
        it_excpbind.
  ENDIF.
  MOVE sy-subrc TO rv_subrc.
ENDMETHOD.

Die Typen der beiden internen Tabelle IT_PARMBIND und IT_EXCPBIND sind im Type-Pool ABAP definiert und dienen dazu, die Parameter an einen Funktionsbaustein vollständig generisch übergeben zu können. Die eigentliche Ermittlung des aufzurufenden Bausteins findet in der Methode GET_FUNCTION_BY_BDOMAIN statt.

ArtParameterTyp
IMPORTINGVALUE( IV_BDOMAIN )ZCD_BDOMAIN_FUNC-BDOMAIN
IMPORTINGVALUE( IV_EVENT )ZCD_BDOMAIN_FUNC-EVENT
RETURNINGVALUE( RV_FUNCTION )ZCD_BDOMAIN_FUNC-FUNCTION
METHOD get_function_by_domain.
  DATA:
    ref_bdomain_func TYPE REF TO zcd_bdomain_func,
    ls_tfkfbm        TYPE tfkfbm,
    ref_func         TYPE REF TO tfkfbc,
    lt_func          LIKE TABLE OF ref_func->*.
  CLEAR rv_function.
  READ TABLE gt_bdomain_func
       REFERENCE INTO ref_bdomain_func
       WITH TABLE KEY event   = iv_event
                      bdomain = iv_bdomain.
  IF sy-subrc NE 0.
    SELECT SINGLE * INTO ls_tfkfbm
           FROM tfkfbm WHERE fbeve = iv_event.
    IF ls_tfkfbm-xmehr EQ abap_true.
      RETURN.
    ENDIF.
    CALL FUNCTION 'FKK_FUNC_MODULE_DETERMINE'
      EXPORTING
        i_fbeve            = iv_event
        i_applk            = 'V'
        i_only_application = abap_true
      TABLES
        t_fbstab           = lt_func.
    READ TABLE lt_func REFERENCE INTO ref_func INDEX 1.
    IF sy-subrc NE 0.
      CREATE DATA ref_func.
      MOVE ls_tfkfbm-funcm TO ref_func->funcc.
    ENDIF.
    CREATE DATA ref_bdomain_func.
    MOVE:
      iv_bdomain      TO ref_bdomain_func->bdomain,
      iv_event        TO ref_bdomain_func->event,
      ref_func->funcc TO ref_bdomain_func->function.
    INSERT ref_bdomain_func->* INTO TABLE gt_bdomain_func.
  ENDIF.
  MOVE ref_bdomain_func->function TO rv_function.
ENDMETHOD.

Der aufzurufende Baustein wird zunächst in der internen Tabelle GT_BDOMAIN_FUNC gesucht. Diese interne Tabelle ist als Instanzattribut definiert (direkte Typeingabe TYPE HASHED TABLE OF ZCD_BDOMAIN_FUNC WITH UNIQUE KEY EVENT BDOMAIN) und wurde im Konstruktor vollständig aus der Customizing-Tabelle ZCD_BDOMAIN_FUNC geladen. Wird dort kein passender Eintrag gefunden, wird geprüft, ob das Ereignis mehrfach implementierbar ist. Ist das Ereignis mehrfach implementierbar, wird die Methode verlassen, da in diesem Fall die FS-CD-Bausteine bereits aufgerufen wurden. Wenn der Zeitpunkt jedoch nur einfach implementierbar ist, muss in diesem Fall, dass kein fachbereichsspezifischer Baustein gefunden wurde, der entsprechende FS-CD- oder FI-CA-Baustein aufgerufen werden. Dazu wird zunächst der Funktionsbaustein FKK_FUNC_MODULE_DETERMINE mit gesetzem Flag I_ONLY_APPLICATION verwendet, um den branchenspezifischen Baustein zu ermitteln. Wird ein Funktionsbaustein gefunden (da es nur ein einfach-implementierbarer Zeitpunkt ist, ist der Zugriff mit INDEX 1 okay), wird dieser, falls nicht der Standardbaustein, als aufzurufender Baustein zurückgegeben, nicht ohne sich diesen vorher in unserer internen Tabelle GT_BDOMAIN_FUNC für das nächste Mal zu merken.

Parameter-Übergabe

Wie oben schon ersichtlich war, werden die beiden internen Tabellen IT_PARMBIND und IT_EXCPBIND verwendet, um Parameter dynamisch zu übergeben und die Exceptions dynamisch zu binden. Ich habe diesen Weg gewählt, um das spätere Coding in den Zeitpunkt-Bausteinen möglichst knapp zu halten und Unittests für die Zeitpunkt-Bausteine zu ermöglichen. Diese Binding-Tabellen werden von der Methode ZIF_CD_EVENT_INVOKER~BUILD_BINDING aufgebaut:

ArtParameterTyp
IMPORTINGVALUE( IV_EVENT )ZCD_BDOMAIN_FUNC-EVENT
EXPORTINGET_PARMBINDABAP_FUNC_PARMBIND_TAB
EXPORTINGET_EXCPBINDABAP_FUNC_EXCPBIND_TAB
METHOD zif_cd_event_invoker~build_binding.
  DATA:
    lv_funcname TYPE rs38l-name,
    lt_exc      TYPE rsfb_exc,
    lt_exp      TYPE TABLE OF rsexp,
    lt_imp      TYPE TABLE OF rsimp,
    lt_cha      TYPE TABLE OF rscha,
    lt_tbl      TYPE TABLE OF rstbl.
  CONCATENATE gc_func_prefix iv_event INTO lv_funcname.
  CALL FUNCTION 'FUNCTION_IMPORT_INTERFACE'
    EXPORTING
      funcname           = lv_funcname
    TABLES
      exception_list     = lt_exc
      export_parameter   = lt_exp
      import_parameter   = lt_imp
      changing_parameter = lt_cha
      tables_parameter   = lt_tbl.
  CALL METHOD build_excpbind
    EXPORTING
      it_rsexc    = lt_exc
    CHANGING
      ct_excpbind = et_excpbind.
  CALL METHOD build_parmbind
    EXPORTING
      it_parm     = lt_exp
      iv_kind     = abap_func_importing
    CHANGING
      ct_parmbind = et_parmbind.
  CALL METHOD build_parmbind
    EXPORTING
      it_parm     = lt_imp
      iv_kind     = abap_func_exporting
    CHANGING
      ct_parmbind = et_parmbind.
  CALL METHOD build_parmbind
    EXPORTING
      it_parm     = lt_cha
      iv_kind     = abap_func_changing
    CHANGING
      ct_parmbind = et_parmbind.
  CALL METHOD build_parmbind
    EXPORTING
      it_parm     = lt_tbl
      iv_kind     = abap_func_tables
    CHANGING
      ct_parmbind = et_parmbind.
ENDMETHOD.

Die Konstante GC_FUNC_PREFIX ist ein Präfix, mit dem der Name des Zeitpunkt-Bausteins gebildet wird (siehe unten).

Die Hilfsmethode BUILD_EXCPBIND hat folgendes Coding:

ArtParameterTyp
IMPORTINGIT_RSEXCRSFB_EXC
CHANGINGCT_EXCPBINDABAP_FUNC_EXCPBIND_TAB
METHOD build_excpbind.
  DATA:
    lr_excp     TYPE REF TO  rsexc,
    ls_excpbind LIKE LINE OF ct_excpbind.
  LOOP AT it_rsexc REFERENCE INTO lr_excp.
    MOVE:
      lr_excp->exception TO ls_excpbind-name,
      sy-tabix           TO ls_excpbind-value.
    INSERT ls_excpbind INTO TABLE ct_excpbind.
  ENDLOOP.
ENDMETHOD.

Da die Hilfsmethode BUILD_PARMBIND für Importing-, Exporting- und Changing-Parameter verwendet werden soll, hat der Parameter IT_PARM den Typ ANY TABLE und der Zugriff erfolgt dynamisch mit Feldsymbolen:

ArtParameterTyp
IMPORTINGIT_PARMANY TABLE
IMPORTINGIV_KINDABAP_FUNC_PARMBIND-KIND
CHANGINGCT_PARMBINDABAP_FUNC_PARMBIND_TAB
METHOD build_parmbind.
  DATA:
    ls_parmbind LIKE LINE OF ct_parmbind.
  FIELD-SYMBOLS:
    <parameter> TYPE ANY,
    <name>      TYPE ls_parmbind-name.
  LOOP AT it_parm ASSIGNING <parameter>.
    ASSIGN COMPONENT 'PARAMETER' OF STRUCTURE <parameter> TO <name>.
    ASSERT sy-subrc EQ 0.
    MOVE:
      <name>  TO ls_parmbind-name,
      iv_kind TO ls_parmbind-kind.
    INSERT ls_parmbind INTO TABLE ct_parmbind.
  ENDLOOP.
ENDMETHOD.

Somit haben wir die beiden internen Binding-Tabellen schon mal gefüllt. Es fehlen lediglich noch die Referenzen auf die konkreten Datenobjekte (Attribut ABAP_FUNC_PARMBIND-VALUE), die erst in den Zeitpunkt-Bausteinen ergänzt werden können.

Die Zeitpunkt-Bausteine

Wie oben schon erwähnt, benötigen wir für jeden relevanten Zeitpunkt einen eigenen Zeitpunkt-Funktionsbaustein. Diese werden angelegt, in dem die entsprechenden SAP-Musterbausteine in eine (oder je nach Anzahl der Bausteine ggf. mehrere) kundeneigene Funktionsgruppen kopiert werden. Ich verwende hier die Funktionsgruppe ZF_CD_DOMAIN_FILTER, und als Namenskonvention für die Zeitpunkt-Bausteine verwende ich Z_CD_FILTER_EVENT_{Zeitpunkt}, also z. B. Z_CD_FILTER_EVENT_1102. Die Konstante GC_FUNC_PREFIX in der Klasse ZCL_CD_EVENT_INVOKER wird also auf 'Z_CD_FILTER_EVENT_' gesetzt.

In jedem Baustein gilt es, den Fachbereich aus den verfügbaren Informationen mittels der Klasse ZCL_CD_DOMAIN_DERIVE zu ermitteln sowie mittels der Klasse ZCL_CD_EVENT_INVOKER die Parameter und die Exceptions zu binden und den fachbereichsspezifischen Baustein zu ermitteln und aufzurufen.

Die Objekte der beiden Klassen werden im TOP-Include der Funktionsgruppe erzeugt und gehalten:

DATA:
  gr_derive TYPE REF TO zif_cd_domain_derive,
  gr_invoke TYPE REF TO zif_cd_event_invoker.
LOAD-OF-PROGRAM.
  IF gr_derive IS INITIAL.
    CREATE OBJECT gr_derive TYPE zcl_cd_domain_derive.
  ENDIF.
  IF gr_invoke IS INITIAL.
    CREATE OBJECT gr_invoke TYPE zcl_cd_event_invoker.
  ENDIF.

Das ABAP-Ereignis LOAD-OF-PROGRAM ist so etwas wie der Konstruktor einer Funktionsgruppe. Die Objekte werden nur erzeugt, sofern sie noch nicht vorhanden sind, z. B. durch einen Unit-Test. Dadurch wird es möglich, für Unit-Tests Mocks anstelle der beiden Klassen zu verwenden.

Das Parameter-Binding und der Aufruf des fachbereichsspezifischen Bausteins habe ich in einem Makro im Top-Include zusammengefasst, das in jedem der Zeitpunkt-Bausteine verwendet werden kann:

DEFINE invoke_event.
  data:
    lt_parm  type abap_func_parmbind_tab,
    lr_parm  type ref to abap_func_parmbind,
    lt_excp  type abap_func_excpbind_tab,
    lv_tab   type string,
    lv_subrc type sysubrc.
  field-symbols:
    <fs> type any.
  call method gr_invoke->build_binding
    exporting
      iv_event    = &2
    importing
      et_parmbind = lt_parm
      et_excpbind = lt_excp.
  loop at lt_parm reference into lr_parm.
    if lr_parm->kind eq abap_func_tables.
      assign (lr_parm->name) to <fs>.
      get reference of <fs> into lr_parm->tables_wa.
      concatenate lr_parm->name '[]' into lv_tab.
      assign (lv_tab) to <fs>.
      get reference of <fs> into lr_parm->value.
    else.
      assign (lr_parm->name) to <fs>.
      get reference of <fs> into lr_parm->value.
    endif.
  endloop.
  lv_subrc = gr_invoke->invoke_event(
      iv_domain   = &1
      iv_event    = &2
      it_parmbind = lt_parm
      it_excpbind = lt_excp ).
  move lv_subrc to sy-subrc.
END-OF-DEFINITION.

In diesem Makro wird zunächst die Bindung-Tabellen mit der Methode BUILD_BINDING gefüllt. Danach werden Referenzen auf die Datenobjekte in der Parameter-Tabelle ergänzt – bei internen Tabellen auch die Kopfzeile (Workarea). Diese Datenobjekte sind nur im Rumpf des Zeitpunkt-Bausteins sichtbar, daher die Lösung mit dem Makro. Abschließend wird der fachbereichspezifische Baustein über die Methode INVOKE_EVENT ermittelt und aufgerufen.

Abschließend das Coding des Zeitpunkts 1102:

FUNCTION z_cd_filter_event_1102.
*"----------------------------------------------------------------------
*"*"Lokale Schnittstelle:
*"  IMPORTING
*"     REFERENCE(I_FKKOP) LIKE  FKKOP STRUCTURE  FKKOP
*"     REFERENCE(I_FKKKO) LIKE  FKKKO STRUCTURE  FKKKO OPTIONAL
*"     REFERENCE(I_CALLID) LIKE  CALLID STRUCTURE  CALLID OPTIONAL
*"  EXPORTING
*"     REFERENCE(E_FKKOP) LIKE  FKKOP STRUCTURE  FKKOP
*"  TABLES
*"      T_MESSA STRUCTURE  FIMSG OPTIONAL
*"----------------------------------------------------------------------
  DATA lv_bdomain TYPE zcd_bdomain_func-bdomain.
  lv_bdomain = gr_derive->get_bdomain_by_vktyp( i_fkkop-vktyp ).
  invoke_event lv_bdomain '1102'.
ENDFUNCTION.

Ziemlich übersichtlich, oder? Als kleine Performance-Optimierung habe ich alle Parameter als Call-by-Reference definiert. Das ist hier unkritisch, da die Daten nicht verändert werden. Bei der Übergabe an den fachbereichsspezifischen Funktionsbaustein wird - je nach Einstellung - wieder Call-by-Value verwendet.

Zusammenfassung

Mit diesem Framework ist es möglich, die Anforderungen der Fachbereiche zu trennen und so Abhängigkeiten zu vermeiden. Im Einzelfall kann es in bestimmten Zeitpunkten schwierig sein, den Fachbereich wie vorgesehen zu ermitteln, da die übergebenen Informationen nicht ausreichen. Ggf. sind zusätzliche Zugriffe auf weitere Tabellen, z. B. den Zahlungsstapel oder die Parameter einer Massenaktivität erforderlich, um die benötigten Informationen für die Ableitung zu finden. In manchen Fällen ist es erforderlich, sich Informationen zwischen bestimmten Zeitpunkten zu merken, da bestimmte Zeitpunkte in einer Sequenz prozessiert werden (z. B. 0010, 0020 und 0030). Über den Verwendungsnachweis der SAP-Musterbausteine kann man in der Regel die Aufrufstellen in FI-CA bzw. FS-CD finden, um über eine Analyse des SAP-Codings weitere Möglichkeiten zur Ableitung des Fachbereichs zu finden.

Ein wesentliches Merkmal der vorgestellten Lösung ist die Unittest-Fähigkeit. Dies wird ermöglicht durch die Kapselung der eigentlichen Logik in Klassen, mit denen nur über Interfaces kommuniziert wird. Dadurch können die beiden beteiligten Klassen und die Zeitpunkt-Bausteine voneinander isoliert getestet werden, was gerade bei einem Querschnittsthema wie diesem sehr wichtig ist.


Zurück zur Liste < Dependency-Injection mit ABAP Objects