
RAP - Änderungsbelege implementieren (Manuell)
In diesem Artikel steigen wir in die manuelle Implementierung der Änderungsbelege in unser RAP Objekt ein und schauen uns die verschiedenen Schritte der Integration an. Am Ende sollen Änderungsbelege automatisch erzeugt werden.
Inhaltsverzeichnis
Wir werden zusammen die im letzten Artikel angelegten Änderungsbelege im System integrieren und unsere RAP Anwendung mit der Funktion ausstatten.
Einleitung
Aktuell werden noch keine Änderungsbelege in unserer Sales App geschrieben. Im letzten Artikel haben wir die Grundlage für das Logging geschaffen und erste Einträge manuell erstellt. Für die Implementierung gibt es aktuell zwei mögliche Wege und wir schauen uns in diesem Artikel die manuelle Implementierung im RAP Objekt an.
Verhalten
Aktuell befinden wir uns in einem Managed Szenario und alle Änderungen werden automatisch vom RAP Framework durchgeführt. Daher müssen wir die Speicherlogik erweitern, um eine Möglichkeit zu haben, um auf die Datensätze und die Änderungen reagieren. Dazu erweitern wir die Verhaltensdefinition ZBS_R_SASALE und fügen bei den Knoten SASale und SASeller den Zusatz WITH ADDITIONAL SAVE hinzu. Im Anschluss können wir die Implementierung anlegen lassen und wir erhalten die Saver-Klasse in der Verhaltensimplementierung.
CLASS lsc_zbs_r_sasale DEFINITION INHERITING FROM cl_abap_behavior_saver.
PROTECTED SECTION.
METHODS
save_modified REDEFINITION.
ENDCLASS.
CLASS lsc_zbs_r_sasale IMPLEMENTATION.
METHOD save_modified.
ENDMETHOD.
ENDCLASS.
In die Methode SAVE_MODIFIED erhalten wir verschiedene Strukturen, welche die Änderungen aus der Interaktionsphase enthalten. Auf diese Änderungen können wir zugreifen und diese später auswerten.
Schauen wir uns als Beispiel einmal die CREATE Struktur an. Dort finden wir aktuell zwei Entitäten mit allen Änderungen. Durch die Definition des ADDITIONAL SAVE wurden diese Entitäten übernommen und uns die Informationen geliefert. Die anderen Entitäten im RAP Objekt sind hier aktuell nicht zu finden, da wir sie nicht benötigten.
Implementierung
Da wir nun die Grundlage im RAP Objekt geschaffen haben, können wir die eigentliche Logik für die Erzeugung der Änderungsbelege implementieren.
Hilfsmethoden
Da wir die WRITE Logik für die verschiedenen Entitäten aufrufen wollen, legen wir jeweils eine Hilfsmethode pro Entität im RAP Objekt an. Dabei übergeben wir die alten und neuen Daten, sowie den Änderungsindikator, also welche Änderung durchgeführt wurde. Die Methode für Sale übernimmt alle Informationen und die Objekt-ID wird aus dem Schlüssel erzeugt, um die Instanz zu identifizieren.
METHOD create_clog_sale.
DATA object_id TYPE if_chdo_object_tools_rel=>ty_cdobjectv.
IF new IS NOT INITIAL.
object_id = new-client && new-uuid.
ELSE.
object_id = old-client && old-uuid.
ENDIF.
zcl_zbs_co_sales_chdo=>write( objectid = object_id
utime = cl_abap_context_info=>get_system_time( )
udate = cl_abap_context_info=>get_system_date( )
username = CONV #( cl_abap_context_info=>get_user_technical_name( ) )
o_zbs_sasale = old
n_zbs_sasale = new
upd_zbs_sasale = change_indicator ).
ENDMETHOD.
Die Methode für die Seller macht fast die gleichen Schritte, hat aber unterschiedliche Eingangstypen und Parameter. Als Schlüssel übernehmen wir hier die Parent-ID, um auf den übergeordneten Eintrag zu referenzieren.
METHOD create_clog_seller.
DATA object_id TYPE if_chdo_object_tools_rel=>ty_cdobjectv.
IF new IS NOT INITIAL.
object_id = new-client && new-parent_uuid.
ELSE.
object_id = old-client && old-parent_uuid.
ENDIF.
zcl_zbs_co_sales_chdo=>write( objectid = object_id
utime = cl_abap_context_info=>get_system_time( )
udate = cl_abap_context_info=>get_system_date( )
username = CONV #( cl_abap_context_info=>get_user_technical_name( ) )
o_zbs_saseller = old
n_zbs_saseller = new
upd_zbs_saseller = change_indicator ).
ENDMETHOD.
CREATE
Für Create übergeben wir die einzelnen neuen Datensätze an die beiden Methoden. Der alte Wert ist jeweils leer, da wir keine Referenz haben. Den neuen Wert müssen wir zuerst von den CDS Entität auf die Tabelle mappen, dafür verwenden wir den Zusatz MAPPING FROM ENTITY, der das definierte Mapping im RAP Objekt verwendet. Den Change Indicator setzen wir auf Insert (I).
LOOP AT create-sasale INTO DATA(new_sale).
create_clog_sale( old = VALUE #( )
new = CORRESPONDING #( new_sale MAPPING FROM ENTITY )
change_indicator = 'I' ).
ENDLOOP.
LOOP AT create-saseller INTO DATA(new_seller).
create_clog_seller( old = VALUE #( )
new = CORRESPONDING #( new_seller MAPPING FROM ENTITY )
change_indicator = 'I' ).
ENDLOOP.
UPDATE
Beim Update müssen wir zuerst den aktuellen Datensatz von der Datenbank lesen. Da wir nicht mehr in der Interaktionsphase sind, können wir hier kein EML verwenden, sondern müssen auf den klassischen SELECT zurückgreifen. Beim Mapping der Daten müssen wir diese Mal allerdings etwas abweichend vorgehen, da im Updatefall nicht immer alle Felder in den Daten befüllt sind, sondern nur die Schlüssel und die aktualisierten Informationen. Daher verwenden wir neben dem MAPPING FROM ENTITY auch den Zusatz USING CONTROL, damit die Control Struktur berücksichtigt wird.
LOOP AT update-sasale INTO DATA(update_sale).
SELECT SINGLE FROM zbs_sasale
FIELDS *
WHERE uuid = @update_sale-uuid
INTO @DATA(actual_sale).
create_clog_sale(
old = actual_sale
new = CORRESPONDING #( BASE ( actual_sale ) update_sale MAPPING FROM ENTITY USING CONTROL )
change_indicator = 'U' ).
ENDLOOP.
LOOP AT update-saseller INTO DATA(update_seller).
SELECT SINGLE FROM zbs_saseller
FIELDS *
WHERE uuid = @update_seller-uuid
INTO @DATA(actual_seller).
create_clog_seller(
old = actual_seller
new = CORRESPONDING #( BASE ( actual_seller ) update_seller MAPPING FROM ENTITY USING CONTROL )
change_indicator = 'U' ).
ENDLOOP.
Schauen wir uns dazu einmal einen Beispielsatz im Update an. Hier finden wir die geänderten Felder und den Schlüssel des Datensatzes. Im unteren Bereich findest du die %CONTROL Struktur und die Felder, die geändert wurden. Mit diesen Informationen und als Basis die Originaldaten, können wir den geänderten Datensatz abmischen.
DELETE
Das Löschen ist dann wieder recht ähnlich wie bereits der Insert, außer das wir in diesem Fall den Change Indicator auf Delete (D) setzen und nur einen bestehenden Datensatz haben, den wir an die Logik übergeben.
LOOP AT delete-sasale INTO DATA(delete_sale).
create_clog_sale( old = CORRESPONDING #( delete_sale MAPPING FROM ENTITY )
new = VALUE #( )
change_indicator = 'D' ).
ENDLOOP.
LOOP AT create-saseller INTO DATA(delete_seller).
create_clog_seller( old = CORRESPONDING #( delete_seller MAPPING FROM ENTITY )
new = VALUE #( )
change_indicator = 'D' ).
ENDLOOP.
Test
Führen wir nun einen Test in unserer Anwendung aus. Dazu erstellen wir einen neuen Datensatz, ändern diesen und löschen ihn zum Abschluss. Damit können wir alle Szenarien einmal testen und uns das Ergebnis anschauen.
Auswertung
Um uns gleich die Auswertungen anzuschauen, erweitern wir unsere Testklasse ZCL_BS_DEMO_RAP_SALES_CHANGE um eine zusätzliche Methode, um uns die Änderungen des aktuellen Tages auszugeben.
cl_chdo_read_tools=>changedocument_read(
EXPORTING i_objectclass = zcl_zbs_co_sales_chdo=>objectclass
i_date_of_change = cl_abap_context_info=>get_system_date( )
IMPORTING et_cdredadd_tab = DATA(db_changes) ).
out->write( db_changes ).
Anlegen
Legen wir einen neuen Datensatz in der Anwendung an und verwenden dazu die Factory Actions, die wir bereits in Übungen davor angelegt haben. Dazu definieren wir einen Festwert als Differenz, einen Partner und ein Verkaufsdatum.
Dazu passen wir noch den Verkaufsbetrag und die Währung an und setzen eine Information. Damit wäre der allgemeine Teil der Daten vollständig.
Da wir auch ein Logging auf Ebene der Verkäufer haben, legen wir noch zusätzlich zwei Einträge auf dieser Ebene an. Hier vergeben wir den Anteil 50:50 an die jeweiligen Verkäufer.
Zum Abschluss lassen wir den Datensatz über den "Create" Button anlegen und speichern. Beim Speichern wird nun unsere zusätzliche Logik durchlaufen und die Änderungsbelege geschrieben. Über unsere Auswertung können wir nun prüfen, welche Datensätze im System angelegt wurden.
Da wir eine Einzelsatzverarbeitung im Änderungsobjekt aktiviert haben, erhalten wir für eine Neuanlage drei verschiedene Änderungsnummern. Diese könnten wir mit der Option zusammenführen und müssten dafür alle Änderungen sammeln und an die Logik übergeben. Weiterhin finden wir alle Informationen zu den Tabellen und Feldern wieder, die wir in den Strukturen loggen. Da nicht alle Felder aktiv sind, werden diese Informationen nicht angezeigt.
Ändern
Im nächsten Schritt ändern wir in der Anwendung die Informationen. Dazu passen wir den Verkaufspreis an und ändern die beiden Quoten der Verkäufer. Im Anschluss sichern wir den Datensatz und lassen die Logik durchlaufen. Schauen wir uns die neuen Datensätze an, haben wir wieder neue Änderungsbelege. Diesen weisen nun das Update des Datensatzes auf, ebenso sehen wir den alten Wert und den neuen Wert.
Löschen
Zum Abschluss löschen wir dann den Datensatz aus dem System. Es wird nun ein Löschsatz erzeugt, sodass wir den alten Schlüssel noch im Protokoll sehen, welcher von der Datenbank entfernt wurde.
Tipp
Möchtest du alle Änderungen in einem Änderungsbeleg bündeln, was auch Sinn ergibt, dann musst du für die einzelnen Tabellen und Strukturen die Einstellung "Log Multiple Changes" im Change Document aktivieren. Damit kannst du alle Änderungen sammeln und mit einem Aufruf übergeben. Damit werden die verschiedenen Einträge in einem Änderungsbeleg zusammengefasst und sind als eine Änderung klassifizierbar.
Vollständiges Beispiel
Das vollständige Beispiel findest du in GitHub im entsprechenden Paket für die Sales App. Die Änderungen aus diesem Artikel findest du in diesem Commit und kannst damit die Änderungen, plus die Zusatzinformationen, nachvollziehen.
Fazit
Die manuelle Implementierung in einem RAP Objekt ist nicht ganz einfach und benötigt jede Menge Zusatzcode. Grundsätzlich kann damit aber auch in älteren Releases die Funktion implementiert werden. In einem der nächsten Artikel, werden wir eine modernere Implementierung auf aktuellen Releases zeigen.








