This is a test message to test the length of the message box.
Login
|
ABAP in der Praxis Setup App
Erstellt von Software-Heroes

ABAP in der Praxis - Setup App erstellen

187

Keine SAP GUI mehr? Wie erstellen wir dann eigentlich eine statische Setup App und bleiben auf ABAP Seite trotzdem flexibel für die weitere Implementierung? Hier ein praktisches Beispiel.

Werbung


In diesem Artikel schauen wir uns an, wie wir eine einfache statische Setup-App erstellen können, um zum Beispiel verschiedene Konfigurationsschritte automatisiert übernehmen zu können.

 

Einleitung

In unserem aktuellen Projekt im Clean Core Mesaurement (CCM) gibt es für die Installation einige Schritte zu beachten und durchzuführen. Da diese Schritte sehr zahlreich sind, von der Anlage über das Customizing bis hin zur Konfiguration von Anwendungen und technischen Einstellungen, haben wir uns dazu entschlossen, eine einfache Setup-Anwendung zur Verfügung zu stellen, die die verschiedenen Schritte automatisieren soll. Dabei haben wir keine Möglichkeit, einen Report zur Verfügung zu stellen, sondern müssen mit einer Fiori-Elements-Anwendung arbeiten.

 

Aufgabe

Die Aufgabe besteht nun darin, die eigentliche Anwendung zur Verfügung zu stellen, zu gestalten und ein vernünftiges Pattern dafür zu wählen. Dafür haben wir verschiedene Anforderungen an unsere Anwendung, die wir umsetzen wollen:

  • Erstens sollte die Anwendung eine statische Liste von Setup-Schritten haben, die in einer gewissen Reihenfolge vorgegeben wird und dem User nicht erlaubt, diese zu sortieren, zu filtern oder auszublenden, diese Schritte sind jeweils fest vorgegeben.
  • Im zweiten Schritt sollte die Anwendung modular sein. Das heißt, die einzelnen Setup-Schritte sollen in separaten Logiken abgebildet werden, die aber möglichst gleiche Schritte implementieren, wie zum Beispiel die Prüfung, ob der Schritt erfolgreich war, oder entsprechend die Ausgabe eines Protokolls, von Meldungen und eines Status, welche Einstellungen noch fehlen.
  • Am Ende wird ebenso eine Automatisierung benötigt, sodass der User im Frontend auf „Ausführen“ klicken kann, damit das Setup gestartet wird und die nötigen Schritte im System vornimmt.

 

Hinweis: Im nächsten Abschnitt werden wir auf die Lösung eingehen, wenn du die Aufgabe erst einmal selbstständig machen möchtest, solltest du hier pausieren.

 

Lösung

Gehen wir in diesem Kapitel die Lösung an und schauen uns verschiedene Punkte und Herausforderungen an, die es bei der Umsetzung der Anwendung zu bedenken gibt.

 

Grundlage 

Die Grundlage wird hierbei vor allem eine Custom Entity sein, da wir die Hauptlogik in Klassen verschalen werden, um dort verschiedene Bestandteile in unserem Framework anzulegen oder auch Standard APIs in SAP aufzurufen. Da bietet es sich am einfachsten an, eine Custom Entity zu definieren, da diese als Grundlage keine Daten bzw. Tabelle hat. Hierfür können wir das Custom Pattern verwenden, welches wir im weiteren Artikel beschreiben.

 

Entität

Als Grundlage definieren wir nun die Struktur, die wir dem User ausgeben wollen, damit er mit dem Setup interagieren kann. Dafür benötigen wir eine ID des eigentlichen Schrittes, die identifiziert, welcher Schritt aufgerufen wurde und später zum Beispiel auch die Instanziierung der richtigen Entität durchführt. Der Schritt bekommt eine Beschreibung, einen Criticality-Indikator, damit wir auf einen Blick sehen, ob der Schritt grün ist oder vielleicht noch Nacharbeiten notwendig sind, und eine Statusmeldung, die aussagen soll, wie der aktuelle Zustand ist. Zusätzlich legen wir noch Navigationsoptionen an. Diese sollen später einmal ermöglichen, mit einem Klick in das entsprechende Ziel abzuspringen, wenn du zum Beispiel manuelle Konfigurationen oder eine Überprüfung durchführen möchtest. So könnte entsprechend jede Zeile auf eine andere App navigieren.

@EndUserText.label: 'Setup Steps'
@ObjectModel.query.implementedBy: 'ABAP:ZCL_BC_CCM_SETUP_QUERY'
define root custom entity ZBC_R_CCMSetupSteps
{
  key StepID            : abap.char(2);
      StepDescription   : abap.sstring(60);
      StatusCriticality : abap.int1;
      StatusMessage     : abap.sstring(250);
      NavigationObject  : abap.sstring(200);    
      NavigationAction  : abap.sstring(50);
}

 

Framework

Als Grundlage für die Arbeit verwenden wir eine Factory, die uns über die Identifikation die passende Implementierung erzeugt. Der Vorteil ist, dass wir später nur die Factory aufrufen müssen, einen Identifikator übergeben und über ein entsprechendes Interface dann die eigentliche Implementierung aufrufen können. Weitere Informationen zu diesem Pattern findest du im verlinkten Artikel. Deshalb ist der erste Schritt erst einmal, ein gemeinsames Interface zu definieren, das dann später aufgerufen wird, wenn wir die Aktion ausführen.

INTERFACE zif_bc_ccm_setup_step
  PUBLIC.

  TYPES step_type TYPE c LENGTH 2.
  TYPES:
    BEGIN OF ENUM steps STRUCTURE step BASE TYPE step_type,
      placeholder      VALUE IS INITIAL,
      setting          VALUE 'SE',
      provider_config  VALUE 'PC',
      comm_arrangement VALUE 'CA',
      jobs             VALUE 'JO',
      cluster          VALUE 'CL',
      role             VALUE 'RO',
    END OF ENUM steps STRUCTURE step.

  METHODS check
    RETURNING VALUE(result) TYPE check_result.

  METHODS execute
    IMPORTING cid_ref       TYPE abp_behv_cid
    RETURNING VALUE(result) TYPE execute_result.

  METHODS get_description
    RETURNING VALUE(result) TYPE ZBC_R_CCMSetupSteps-StepDescription.

  METHODS get_step_id
    RETURNING VALUE(result) TYPE ZBC_R_CCMSetupSteps-StepID.

  METHODS get_navigation
    RETURNING VALUE(result) TYPE navigation_result.

  METHODS execute_save
    RETURNING VALUE(result) TYPE REF TO zif_bc_ccm_mini_log.
ENDINTERFACE.

 

Das Interface enthält bestimmte Bestandteile, die wir später auch bei der Verarbeitung benötigen. Alle möglichen Schritte legen wir als Enumeration ab. Dies stellt auch sicher, dass automatisch eine Evaluierung stattfindet, sobald wir eine neue Implementierung machen.

  • Als Methoden implementieren wir CHECK. Hierüber wird die Prüfung durchgeführt, ob ein Schritt erfolgreich durchgeführt oder abgeschlossen wurde.
  • Wir haben eine EXECUTE-Methode, die ausgeführt wird, wenn der User auf Durchführen klickt.
  • Zusätzlich legen wir noch einen EXECUTE-Schritt für SAVE an, der in der Save Sequence aufgerufen wird, wenn wir Schritte haben, die nicht in der eigentlichen Aktion möglich sind, sondern erst in der Save Sequence aufgerufen werden müssen.
  • Dann haben wir weitere Methoden, die zum Beispiel die Beschreibung zurückgeben, den Schritt als Character-Feld oder die Navigation auf die entsprechende Ziel-App, die pro Schritt unterschiedlich sein kann.

 

In der Factory legen wir dann eine Methode mit der Konfiguration an, die wir zentral aufrufen können. Hier benötigen wir die Logik in zwei Schritten: einmal zur Erzeugung der Daten in der Query-Klasse und einmal zur Erzeugung der Instanzen über die Factory. Deshalb legen wir hier eine kleine Mapping-Tabelle an, welche die Step-ID intern, also die Enumeration, und eine externe ID hält, die wir über den Service konsumieren, sowie die eigentliche Instanz der Implementierung.

METHOD get_step_configuration.
  result = VALUE #( ( step_id  = zif_bc_ccm_setup_step=>step-role
                      instance = NEW zcl_bc_ccm_step_role( ) )
                    ( step_id  = zif_bc_ccm_setup_step=>step-setting
                      instance = NEW zcl_bc_ccm_step_setting( ) )
                    ( step_id  = zif_bc_ccm_setup_step=>step-provider_config
                      instance = NEW zcl_bc_ccm_step_provider( ) )
                    ( step_id  = zif_bc_ccm_setup_step=>step-comm_arrangement
                      instance = NEW zcl_bc_ccm_step_comm_arr( ) )
                    ( step_id  = zif_bc_ccm_setup_step=>step-jobs
                      instance = NEW zcl_bc_ccm_step_jobs( ) )
                    ( step_id  = zif_bc_ccm_setup_step=>step-placeholder
                      instance = NEW zcl_bc_ccm_step_placeholder( ) )
                    ( step_id  = zif_bc_ccm_setup_step=>step-cluster
                      instance = NEW zcl_bc_ccm_step_cluster( ) ) ).

  LOOP AT result REFERENCE INTO DATA(step).
    step->external_id = step->instance->get_step_id( ).
  ENDLOOP.
ENDMETHOD.

 

Damit haben wir die Grundlage geschaffen, um später flexibel neue Schritte hinzuzufügen und diese nur an einer Stelle pflegen zu müssen. Die Konfiguration wird dann in der Klasse aufgerufen oder in der Factory, was sie für uns zentral verwaltbar macht.

 

Service 

Um den Service aufzubauen, müssen wir erst einmal die Grundlage für die Daten schaffen. Da wir in einem Custom-Szenario unterwegs sind, legen wir dafür eine neue Query-Klasse an, die uns die Daten beschaffen soll. Grundsätzlich müssen wir dazu mehrere Annahmen treffen, die später zutreffen. So wollen wir zum Beispiel alle Schritte vollständig darstellen, keine Filterung erlauben und kein Sortieren der Einträge. Grundsätzlich sind einige Voraussetzungen bereits geschaffen: Dadurch, dass wir eine Custom Entity verwenden, sind die Felder virtuell und müssen im UI manuell erst aktiviert werden.

Der erste Schritt, den wir durchführen, ist es, aus dem Request die Pflichtmethoden aufzurufen, da wir sonst bei der Implementierung einen Fehler erhalten. Dann lesen wir uns zusätzlich noch den Filter der Step-ID aus und geben diesen als Filter zurück. Dies liegt daran, dass zum Beispiel bei der Ausführung einer Aktion immer die aktuelle Zeile gelesen wird, bevor die Aktion ausgeführt wird. Deshalb benötigen wir zumindest die Step-ID, also den Schlüssel, den wir als Filter verwenden können.

request->get_sort_elements( ).
request->get_paging( ).

TRY.
    DATA(odata_filter) = request->get_filter( )->get_as_ranges( ).
    result = CORRESPONDING #( odata_filter[ name = 'STEPID' ]-range ).

  CATCH cx_rap_query_filter_no_range cx_sy_itab_line_not_found.
    CLEAR result.
ENDTRY.

 

Im nächsten Abschnitt der Implementierung können wir dann über die Konfiguration laufen und beachten dabei auch den Filter, den wir übergeben bekommen haben. So erstellen wir eine Liste der Schritte, die in unserer Anwendung später zur Verfügung stehen. Ebenso führen wir hier schon erste Bestandteile über das Interface aus, wie zum Beispiel, dass wir einen Check durchführen, um das Ergebnis zu erhalten und in der Zeile zu speichern. Ebenso übernehmen wir Informationen wie die Step-ID, die Beschreibung und den aktuellen Status, den wir aus dem Schritt zurückbekommen, um beim initialen Laden schon erste Informationen bei uns vorzuhalten.

LOOP AT configs INTO DATA(config) WHERE step_id IN step_filter.
  DATA(table_line_number) = sy-tabix.

  IF io_request->is_data_requested( ).
    DATA(check_result) = config-step->check( ).
  ENDIF.

  DATA(navigation) = config-step->get_navigation( ).

  INSERT VALUE #( StepID            = config-step->get_step_id( )
                  StepDescription   = |{ table_line_number }. { config-step->get_description( ) }|
                  StatusCriticality = check_result-status
                  StatusMessage     = check_result-message
                  NavigationObject  = navigation-object
                  NavigationAction  = navigation-action )
         INTO TABLE steps.
ENDLOOP.

 

Im letzten Schritt müssen wir dann nur noch die Schritte zurückgeben, wenn diese auch entsprechend angefordert werden. Entweder geben wir die Daten zurück oder die Anzahl der Daten, wenn der UI-Service dies bei uns anfragt.

IF io_request->is_data_requested( ).
  io_response->set_data( steps ).
ENDIF.

IF io_request->is_total_numb_of_rec_requested( ).
  io_response->set_total_number_of_records( lines( steps ) ).
ENDIF.

 

Verhalten

Bei der Verhaltensdefinition stellen wir ein unmanaged Szenario ein, da wir mit der Custom Entity arbeiten. Wir setzen alle CRUD-Operationen auf "internal", da wir keine neuen Datensätze über die App anlegen wollen, sondern nur bestehende Datensätze anzeigen. Für das Prüfen und Ausführen legen wir dann jeweils eine neue Aktion an. Diese sind instanzbasiert, da sie pro Schritt erfolgen sollen.

unmanaged implementation in class zbp_bc_ccm_setup_steps unique;
strict ( 2 );

define behavior for ZBC_R_CCMSetupSteps alias StetupSteps
lock master
authorization master ( instance )
{
  internal create;
  internal update;
  internal delete;

  action CheckStep;
  action ExecuteStep;

  side effects {
    action ExecuteStep affects $self;
  }

  field ( readonly ) StepID;
}

 

Bei der Implementierung müssen wir dann jede Aktion einzeln implementieren, können das aber relativ generisch machen, sodass wir zum Beispiel über die verschiedenen Schlüssel laufen, die angesprochen werden, und uns über die Factory anhand der Step-ID die passende Instanz anlegen lassen. Über das Interface rufen wir dann die "EXECUTE"- oder "CHECK"-Methode auf und übernehmen das Ergebnis in Form von Meldungen.

LOOP AT keys INTO DATA(key).
  DATA(step) = zcl_bc_ccm_setup_step_factory=>create_step( CONV #( key-StepID ) ).
  DATA(step_result) = step->execute( key-%cid_ref ).

  INSERT LINES OF step_result-log->get_all_messages( ) INTO TABLE reported-%other.
  INSERT step INTO TABLE lcl_buffer=>instances.
ENDLOOP.

 

Da wir dann aber auch daran denken müssen, dass wir zum Beispiel auch in der Save Sequence auf diese Instanz zugreifen müssen, merken wir uns den aktuell angewandten Schritt in einem temporären Puffer und rufen dann in der Save Sequence die zweite Methode auf. Das Handling muss dann die Implementierung übernehmen.

 

UI

Für das UI müssen wir dann noch zusätzliche UI-Annotationen hinzufügen. Da wir uns in einem Custom-Szenario befinden, müssen wir diese auch in der Custom Entity ergänzen, zusammen mit den eigentlichen Definitionen. Dafür definieren wir uns auf Zeilenebene eine Criticality, um die Schritte mit farblichen Hervorhebungen zu versehen. Wir deaktivieren den Filter auf der entsprechenden Spalte und definieren uns zusätzlich noch zwei Aktionen, die wir Inline anzeigen wollen, um damit schnelleren Zugriff zu erhalten, sodass diese als Actions nicht in der Leiste oben zu sehen sind.

    @UI.lineItem      : [
      { position      : 10, criticality: 'StatusCriticality' },
      { position      : 30, type: #FOR_ACTION, label: 'Check', dataAction: 'CheckStep', inline: true },
      { position      : 40, type: #FOR_ACTION, label: 'Execute', dataAction: 'ExecuteStep', inline: true }
    ]
    @ObjectModel.text.element: [ 'StepDescription' ]
    @UI.textArrangement:#TEXT_ONLY
    @Consumption.filter.hidden: true
    @EndUserText.label: 'Step'
key StepID            : abap.char(2);

 

Als Ergebnis erhalten wir dann die Schritte mit der Schrittreihenfolge und den Hervorhebungen, wenn ein Schritt zum Beispiel nicht erfüllt ist. Im hinteren Teil finden wir dann die beiden Actions, die inline angezeigt werden, um den Zugriff und die Ausführung zu erleichtern.

 

Über eine zusätzliche Annotation für den Filter können wir diesen auch aus der Liste der filterbaren Felder ausschließen. So hat der User keine Möglichkeit, bestimmte Schritte auszublenden oder Varianten mit Filtern vorzubelegen. Damit erreichen wir dann auch, dass die Einträge nicht mehr sortierbar, filterbar und gruppierbar sind (auch wegen der Custom Entity).

@Consumption.filter.hidden: true

 

Generierung

Als Service verwenden wir einen OData-v4-UI-Service. Auf diesen können wir dann auch den Fiori-Elements-Generator „from Template“ starten, um unsere Standard-Fiori-Elements-Anwendung zu generieren. Weitere Informationen zur Erzeugung der Anwendung findest du in dem folgenden Artikel verlinkt. Bei der Generierung verwenden wir das Standard-Template „List Report“, ansonsten haben wir keine weiteren Anpassungen vorgenommen. Möchtest du die Anwendung später deployen, solltest du auch eine Deployment-Konfiguration anlegen, ebenso eine Launchpad-Konfiguration, wenn du später die Anwendung ins Launchpad einbinden möchtest.

 

Object Page

Im nächsten Schritt wollen wir nach der Generierung die Navigation auf die Object Page deaktivieren. Dazu wechseln wir auf die Page Map und können dort die Object Page, die automatisch mitgeneriert wurde, löschen. Damit verschwindet im hinteren Teil der Navigationspfad und wir können nicht navigieren, bleiben also immer auf dem List Report.

 

Filter

Laden wir dann das erste Mal unsere Anwendung im Preview, werden wir feststellen, dass es immer noch im oberen Teil die Möglichkeit der Filter Bar gibt. Hier können wir den Bereich vergrößern oder verkleinern und Elemente hinzufügen. Dadurch, dass wir aber alle Felder für den Filter deaktiviert haben, werden auch keine weiteren Kriterien angeboten. Daher macht dieser Bereich eigentlich keinen Sinn mehr.

 

Dazu wechseln wir in der Page Map in den Editiermodus des List Reports und klicken dort auf die Filter Bar. Auf der rechten Seite sollten wir weitere Einstellungen für die Filter Bar erhalten. Dort können wir die Filter Bar komplett ausblenden, indem wir den Wert auf „true“ setzen.

 

Der Preview wird dann anschließend neu geladen und wir können uns noch einmal im Detail die Einstellungen ansehen. Wir sehen, dass das Variantenmanagement noch vorhanden ist, ebenso die zusätzlichen Aktionen. Der Bereich für die Filter Bar wurde aber nun ausgeblendet, ebenso die zusätzlichen Buttons.

 

Test

Testen wir nun einmal unsere finale und deployte Anwendung. Dabei haben wir bereits einige Schritte implementiert, die für das Setup notwendig sind, das war aber nicht Teil der eigentlichen Aufgabe. Nach dem Laden der Liste sehen wir die verschiedenen Schritte, die durch die Konfiguration der Factory geladen werden. Wir erhalten einen Status, wenn bestimmte Sachen noch nicht vollständig abgeschlossen sind, und Statusmeldungen, ebenso sind diese farblich hervorgehoben. Im nächsten Schritt führen wir eine Prüfung über den CHECK aus. Dieser ruft unsere eigentliche Implementierung für die Einstellungen auf und liefert ein Ergebnis in Form von Meldungen zurück. So wissen wir genau im Detail, welche Schritte alle noch nicht vollständig definiert sind. Zum Abschluss rufen wir über EXECUTE die eigentliche Anlage auf. Die Konfiguration wird durchlaufen, die Parameter werden in der Konfiguration angelegt und es wird automatisch ein Check durchgeführt. Die Einstellungen sind nun grün, alle Parameter sind angelegt.

 

In diesem Fall rufen wir nach dem Execute noch zusätzlich einen Side Effect auf. Dieser kümmert sich darum, dass die Zeile neu geladen wird. Damit erhält der User auch automatisch Feedback, wenn er eine Aktion wählt und sich der Status eines Schrittes ändert.

 

Herausforderung

Ebenso gab es einige Herausforderungen bei der Umsetzung der Anwendung, die zu berücksichtigen sind. Grundsätzlich haben wir nun ein modulares Framework, was die verschiedenen Setup-Schritte kapselt, sodass wir kleine Implementierungen vornehmen können, ohne jedes Mal die komplette Anwendung neu aufbauen zu müssen.

Allerdings gibt es hier auch Herausforderungen bei der Nutzung der APIs. So sind nicht alle APIs, die wir nutzen, zum Beispiel für die Anlage des Communication Arrangements, die Anlage der Business-Rollen oder die Zuordnung zum Business User, automatisch auch für RAP geeignet. Hier musst du dann am Ende prüfen und schauen, welche API sich in welchem Szenario eignet. In einigen Fällen mussten wir auch eine API per bgPF in einen Hintergrundprozess versetzen, damit diese sauber durchlief und den RAP-Flow nicht gestört hat. Hier war meist ein COMMIT im Prozess gewesen, welcher im STRICT Mode verboten ist.

 

Vollständiges Beispiel

Die App verwenden wir bereits in unserem Clean Core Mesaurement (CCM) Projekt. In dem GitHub Repository findest du das komplette Projekt und im Verzeichnis ZBC_CCM_APP_SETUP die Setup App.

 

Fazit

Die Umsetzung der Setup-App ist nicht unbedingt schwer, erfordert aber einige Schritte, die bedacht werden müssen, um die Anwendung zu implementieren. Hierbei ist ein wichtiges Learning, wie wir verschiedene Mechanismen in Fiori Elements deaktivieren können, um dem Anwender eine möglichst einfache Liste zur Verfügung zu stellen. Zweitens lernen wir sehr viel darüber, welche Frameworks und Elemente wir in welchen Situationen aufrufen können und wo wir Workarounds finden müssen.


Enthaltene Themen:
TippABAP in der PraxisRAPCCM
Kommentare (0)



Und weiter ...

Bist du zufrieden mit dem Inhalt des Artikels? Wir posten jeden Dienstag und Freitag neuen Content im Bereich ABAP und unregelmäßig in allen anderen Bereichen. Schaue bei unseren Tools und Apps vorbei, diese stellen wir kostenlos zur Verfügung.


DDIC der Zukunft (Vergleich)

Kategorie - ABAP

Wie geht es eigentlich mit dem DDIC in Zukunft weiter und können wir schon Heute die neuen Features für uns nutzen? In diesem Teil vergleichen wir die Welt von Heute mit der von Morgen und wo du alle Features findest.

03.07.2026

DDIC der Zukunft (Morgen)

Kategorie - ABAP

Wie geht es eigentlich mit dem DDIC in Zukunft weiter und können wir schon Heute die neuen Features für uns nutzen? In diesem Teil werfen wir ein Blick auf Morgen und wie uns CDS Artefakte bei der Modellierung helfen.

30.06.2026

DDIC der Zukunft (Heute)

Kategorie - ABAP

Wie geht es eigentlich mit dem DDIC in Zukunft weiter und können wir schon Heute die neuen Features für uns nutzen? In diesem Teil schauen wir auf den aktuellen Zustand des Dictionary in ABAP.

26.06.2026

ABAP in der Praxis - News App verwenden

Kategorie - ABAP

In diesem Praxisbeispiel schauen wir uns an, wie wir die neue News App im ABAP Environment mit Informationen bestücken können. Dabei analysieren wir die aktuelle ABAP Auslieferung der Anwendung und des Services, um das korrekte Format zu finden.

31.03.2026

ABAP in der Praxis - Objekt Generator

Kategorie - ABAP

In diesem Beispiel schauen wir uns an, wie wir mit der XCO Bibliothek einen wiederverwendbaren Generator erstellen, um uns für unsere Tutorials etwas Arbeit zu sparen und automatisiert DDIC Objekte zu generieren.

09.01.2026