Draft Handling in RAP: Zwischenspeicherung für komplexe Workflows

kategorie
RAP
Veröffentlicht
autor
Johannes

Draft Handling ermöglicht es Benutzern, Änderungen an Business Objects zwischenzuspeichern, ohne sie sofort in die Datenbank zu schreiben. Dies ist besonders wichtig für komplexe Eingabeformulare in Fiori-Anwendungen. Mit Draft Handling können Benutzer ihre Arbeit unterbrechen, zu einem späteren Zeitpunkt fortsetzen und erst dann die Änderungen aktivieren, wenn alle Eingaben vollständig und validiert sind.

In diesem Artikel lernen Sie, wie Draft Handling in RAP funktioniert, wie Sie draft-fähige CDS Views und Behavior Definitions erstellen, und welche Draft Actions für typische Workflows zur Verfügung stehen.

Das Draft-Konzept: Active vs. Draft Instanzen

Das zentrale Konzept beim Draft Handling ist die Unterscheidung zwischen aktiven Instanzen und Draft-Instanzen:

  • Aktive Instanzen (Active Entities): Die persistenten Daten in der Datenbanktabelle. Diese repräsentieren den aktuellen, gültigen Zustand des Business Objects und sind für alle Benutzer sichtbar.

  • Draft-Instanzen (Draft Entities): Temporäre Arbeitskopien, die in einer separaten Draft-Tabelle gespeichert werden. Ein Draft ist nur für den bearbeitenden Benutzer sichtbar und enthält noch nicht validierte oder unvollständige Änderungen.

Der Lebenszyklus eines Drafts sieht typischerweise so aus:

  1. Edit: Benutzer startet die Bearbeitung → System erstellt Draft-Kopie
  2. Modify: Benutzer ändert Daten → Änderungen werden automatisch im Draft gespeichert
  3. Prepare: System führt Validierungen aus → Fehler werden angezeigt, aber Draft bleibt erhalten
  4. Activate: Benutzer aktiviert → Draft wird zur aktiven Instanz, Draft-Eintrag wird gelöscht

Alternativ kann der Benutzer jederzeit Discard wählen, um den Draft zu verwerfen, ohne die aktive Instanz zu ändern.

Wann brauche ich Draft?

Draft Handling ist sinnvoll bei:

  • Komplexen Eingabemasken mit vielen Feldern
  • Mehrstufigen Workflows, bei denen Benutzer zwischenspeichern möchten
  • Langlebigen Transaktionen, die nicht in einer Session abgeschlossen werden
  • Validierungen, die erst bei Aktivierung ausgeführt werden sollen

Draft vs. Non-Draft Szenarien

AspektDraftNon-Draft
SpeicherungSeparate Draft-TabelleDirekt in Datenbanktabelle
ZwischenspeichernJa, automatischNein
ValidierungBei Prepare/ActivateBei jedem Save
KomplexitätHöherNiedriger
Fiori-UXVerbessert (Auto-Save)Standard
Use CaseKomplexe FormulareEinfache CRUD-Apps

Non-Draft Szenario (Standard)

managed implementation in class zbp_i_travel unique;
strict ( 2 );
define behavior for ZI_Travel alias Travel
persistent table ztravel
lock master
authorization master ( instance )
etag master LastChangedAt
{
create;
update;
delete;
}

Draft Szenario

managed implementation in class zbp_i_travel unique;
strict ( 2 );
with draft;
define behavior for ZI_Travel alias Travel
persistent table ztravel
draft table zdraft_travel
lock master total etag LastChangedAt
authorization master ( instance )
etag master LastChangedAt
{
create;
update;
delete;
draft action Edit;
draft action Activate optimized;
draft action Discard;
draft action Resume;
draft determine action Prepare;
}

Exclusive vs. Shared Locks

RAP unterstützt zwei Lock-Modi für Draft-Szenarien:

Lock-TypBeschreibungAnwendungsfall
ExclusiveNur ein Benutzer kann das Objekt bearbeitenStandard für kritische Daten
SharedMehrere Benutzer können Drafts erstellenKollaborative Szenarien

Exclusive Lock (Standard)

Mit Exclusive Lock kann nur ein Benutzer gleichzeitig ein Business Object bearbeiten:

define behavior for ZI_Travel alias Travel
persistent table ztravel
draft table zdraft_travel
lock master total etag LastChangedAt
authorization master ( instance )
{
// Exclusive Lock ist der Standard
// Nur ein Draft pro Instanz erlaubt
}

Shared Lock

Mit Shared Lock können mehrere Benutzer unabhängige Drafts erstellen:

define behavior for ZI_Travel alias Travel
persistent table ztravel
draft table zdraft_travel
lock master total etag LastChangedAt
authorization master ( instance )
late numbering
{
// Shared Draft: Mehrere Benutzer können
// verschiedene Versionen bearbeiten
with unmanaged save;
}

Draft-fähige CDS Views erstellen

Um Draft Handling zu nutzen, müssen Ihre CDS Views bestimmte Voraussetzungen erfüllen. Die wichtigste Anforderung ist ein Total ETag Feld, das Änderungen an der gesamten Entität nachverfolgt.

Interface View mit Draft-Unterstützung

@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Travel - Interface View'
define root view entity ZI_Travel
as select from ztravel
association [0..*] to ZI_Booking as _Booking
on $projection.TravelUUID = _Booking.TravelUUID
{
key travel_uuid as TravelUUID,
travel_id as TravelId,
agency_id as AgencyId,
customer_id as CustomerId,
begin_date as BeginDate,
end_date as EndDate,
@Semantics.amount.currencyCode: 'CurrencyCode'
total_price as TotalPrice,
currency_code as CurrencyCode,
description as Description,
overall_status as OverallStatus,
// Administrative Felder
@Semantics.user.createdBy: true
created_by as CreatedBy,
@Semantics.systemDateTime.createdAt: true
created_at as CreatedAt,
@Semantics.user.lastChangedBy: true
last_changed_by as LastChangedBy,
@Semantics.systemDateTime.lastChangedAt: true
last_changed_at as LastChangedAt,
// Wichtig für Draft: Total ETag für Concurrency Control
@Semantics.systemDateTime.localInstanceLastChangedAt: true
local_last_changed_at as LocalLastChangedAt,
// Associations
_Booking
}

Das Feld LocalLastChangedAt mit der Annotation @Semantics.systemDateTime.localInstanceLastChangedAt wird als Total ETag verwendet. Es ermöglicht dem RAP-Framework, Konflikte bei gleichzeitigen Zugriffen zu erkennen.

Projection View für Draft

Die Projection View muss die Draft-relevanten Felder exponieren:

@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Travel - Projection View'
@Metadata.allowExtensions: true
define root view entity ZC_Travel
provider contract transactional_query
as projection on ZI_Travel
{
key TravelUUID,
TravelId,
AgencyId,
CustomerId,
BeginDate,
EndDate,
TotalPrice,
CurrencyCode,
Description,
OverallStatus,
CreatedBy,
CreatedAt,
LastChangedBy,
LastChangedAt,
LocalLastChangedAt,
_Booking : redirected to composition child ZC_Booking
}

Draft Table Definition

Für jede Entität mit Draft-Unterstützung benötigen Sie eine separate Draft-Tabelle. Diese Tabelle speichert die temporären Arbeitskopien und muss die gleiche Struktur wie die Haupttabelle haben, ergänzt um Draft-spezifische Verwaltungsfelder:

@EndUserText.label : 'Travel Draft'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
define table zdraft_travel {
key client : abap.clnt not null;
key travel_uuid : sysuuid_x16 not null; // Draft-UUID
travel_id : abap.numc(8);
agency_id : abap.numc(6);
customer_id : abap.numc(6);
begin_date : abap.dats;
end_date : abap.dats;
total_price : abap.curr(16,2);
currency_code : abap.cuky;
description : abap.string(256);
status : abap.char(1);
// Draft-spezifische Felder (automatisch vom Framework genutzt)
"%admin" : include sych_bdl_draft_admin_inc;
}

Draft-Admin Include

Das Include sych_bdl_draft_admin_inc enthält wichtige Verwaltungsfelder:

FeldBeschreibung
draftentityuuidEindeutige Draft-ID
draftentitycreationdatetimeErstellungszeitpunkt
draftentitylastchangedatetimeLetzte Änderung
draftentitycreatedbyErstellt von
draftentitylastchangedbyGeändert von
draftentityownershipstateDraft-Besitz
hasactiveentityHat aktive Version
isactiveentityIst aktive Version

Draft Actions

RAP stellt automatisch Draft Actions bereit:

Edit Action

Startet den Bearbeitungsmodus und erstellt einen Draft:

" In der Behavior Definition
draft action Edit;
" EML-Aufruf
MODIFY ENTITIES OF zi_travel
ENTITY Travel
EXECUTE Edit FROM VALUE #( ( TravelId = '00000001' ) )
MAPPED DATA(mapped)
FAILED DATA(failed)
REPORTED DATA(reported).

Activate Action

Aktiviert den Draft und schreibt die Änderungen in die Datenbank:

" In der Behavior Definition
draft action Activate optimized;
" EML-Aufruf
MODIFY ENTITIES OF zi_travel
ENTITY Travel
EXECUTE Activate FROM VALUE #( ( %key-TravelId = '00000001' ) )
MAPPED mapped
FAILED failed
REPORTED reported.
COMMIT ENTITIES.

Der Zusatz optimized sorgt dafür, dass nur geänderte Felder geschrieben werden.

Discard Action

Verwirft den Draft ohne Speicherung:

" In der Behavior Definition
draft action Discard;
" EML-Aufruf
MODIFY ENTITIES OF zi_travel
ENTITY Travel
EXECUTE Discard FROM VALUE #( ( %key-TravelId = '00000001' ) )
FAILED failed
REPORTED reported.

Resume Action

Setzt die Bearbeitung eines vorhandenen Drafts fort:

" In der Behavior Definition
draft action Resume;
" EML-Aufruf
MODIFY ENTITIES OF zi_travel
ENTITY Travel
EXECUTE Resume FROM VALUE #( ( %key-TravelId = '00000001' ) )
RESULT DATA(result)
FAILED failed
REPORTED reported.

Prepare Action

Führt Validierungen aus, ohne zu aktivieren:

" In der Behavior Definition
draft determine action Prepare {
validation validateDates;
validation validateCustomer;
}
" EML-Aufruf - prüft alle Draft-Validierungen
MODIFY ENTITIES OF zi_travel
ENTITY Travel
EXECUTE Prepare FROM VALUE #( ( %key-TravelId = '00000001' ) )
FAILED failed
REPORTED reported.

Vollständiges Draft-Beispiel

1. Behavior Definition

managed implementation in class zbp_i_travel unique;
strict ( 2 );
with draft;
define behavior for ZI_Travel alias Travel
persistent table ztravel
draft table zdraft_travel
lock master total etag LastChangedAt
authorization master ( instance )
etag master LastChangedAt
{
// Standard-Operationen
create;
update;
delete;
// Feldmapping
field ( readonly ) TravelId;
field ( readonly ) CreatedBy, CreatedAt, LastChangedBy, LastChangedAt;
field ( numbering : managed ) TravelId;
// Actions
action ( features : instance ) acceptTravel result [1] $self;
action ( features : instance ) rejectTravel result [1] $self;
// Validations - werden bei Prepare/Activate ausgeführt
validation validateDates on save { field BeginDate, EndDate; }
validation validateCustomer on save { field CustomerId; }
// Determinations
determination setStatusNew on modify { create; }
// Draft Actions
draft action Edit;
draft action Activate optimized;
draft action Discard;
draft action Resume;
draft determine action Prepare {
validation validateDates;
validation validateCustomer;
}
}

2. Projection mit Draft

projection;
strict ( 2 );
use draft;
define behavior for ZC_Travel alias Travel
{
use create;
use update;
use delete;
use action acceptTravel;
use action rejectTravel;
use action Edit;
use action Activate;
use action Discard;
use action Resume;
use action Prepare;
}

3. Draft-Validierung implementieren

CLASS lhc_travel DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
METHODS validateDates FOR VALIDATE ON SAVE
IMPORTING keys FOR Travel~validateDates.
ENDCLASS.
CLASS lhc_travel IMPLEMENTATION.
METHOD validateDates.
" Draft-Daten lesen (nicht aktive Daten!)
READ ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
FIELDS ( BeginDate EndDate )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_travel).
LOOP AT lt_travel INTO DATA(ls_travel).
" Draft-Validierung: Warnung statt Fehler möglich
IF ls_travel-EndDate < ls_travel-BeginDate.
APPEND VALUE #(
%tky = ls_travel-%tky
%element-EndDate = if_abap_behv=>mk-on
) TO failed-travel.
APPEND VALUE #(
%tky = ls_travel-%tky
%state_area = 'VALIDATE_DATES'
%element-EndDate = if_abap_behv=>mk-on
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'Enddatum muss nach Beginndatum liegen'
)
) TO reported-travel.
ENDIF.
ENDLOOP.
ENDMETHOD.
ENDCLASS.

Draft in Fiori Elements

Wenn Draft aktiviert ist, zeigt Fiori Elements automatisch:

  • Auto-Save: Änderungen werden automatisch gespeichert
  • Draft Indicator: Zeigt an, dass ungespeicherte Änderungen existieren
  • Discard-Button: Verwirft alle Änderungen
  • Save-Button: Aktiviert den Draft (führt Validierungen aus)

Concurrent Access und Locking im Detail

Das RAP-Framework verwaltet Locks automatisch, um Datenkonsistenz sicherzustellen. Beim Draft Handling gibt es zwei kritische Szenarien:

Szenario 1: Zwei Benutzer bearbeiten dieselbe Instanz

Mit Exclusive Lock (Standard):

  1. Benutzer A startet Edit → Draft wird erstellt, Lock wird gesetzt
  2. Benutzer B versucht Edit → Fehlermeldung “Objekt wird von Benutzer A bearbeitet”
  3. Benutzer A aktiviert oder verwirft → Lock wird freigegeben
  4. Benutzer B kann nun bearbeiten

Szenario 2: Draft-Timeout

Wenn ein Benutzer einen Draft erstellt und die Session verlässt:

  • Der Draft bleibt in der Draft-Tabelle
  • Bei erneutem Zugriff kann mit Resume fortgesetzt werden
  • Andere Benutzer sind weiterhin blockiert (bei Exclusive Lock)

Um verwaiste Drafts zu vermeiden, können Sie einen Background-Job einrichten, der alte Drafts automatisch löscht:

" Beispiel: Drafts älter als 7 Tage löschen
DELETE FROM zdraft_travel
WHERE draftentitylastchangedatetime < @lv_cutoff_timestamp.

Best Practices

  1. Draft nur wenn nötig - Bei einfachen CRUD-Apps ohne komplexe Formulare ist Draft oft unnötig und erhöht die Komplexität
  2. Validierungen in Prepare - Ermöglicht frühe Rückmeldung ohne Aktivierung, verbessert die Benutzererfahrung
  3. Draft Table korrekt definieren - Immer das Admin-Include sych_bdl_draft_admin_inc verwenden
  4. Optimized Activate - Der Zusatz optimized bei Activate verbessert die Performance, da nur geänderte Felder geschrieben werden
  5. State Areas nutzen - Gruppieren Sie Validierungsmeldungen für bessere UX mit %state_area
  6. Draft Timeout einrichten - Konfigurieren Sie einen Background-Job zum automatischen Löschen alter Drafts
  7. Testing nicht vergessen - Testen Sie alle Draft-Szenarien: Edit, Activate, Discard, Resume und Concurrent Access

Zusammenfassung

Draft Handling ist ein mächtiges Feature des RESTful ABAP Programming Models, das die Benutzererfahrung bei komplexen Datenerfassungsszenarien erheblich verbessert. Die wichtigsten Punkte:

  • Draft vs. Active: Drafts sind temporäre Arbeitskopien, die erst bei Aktivierung zu persistenten Daten werden
  • Draft Table: Eine separate Tabelle speichert die Draft-Instanzen mit dem Admin-Include für Verwaltungsfelder
  • Draft Actions: Edit, Activate, Discard, Resume und Prepare ermöglichen den kompletten Draft-Workflow
  • Locking: Exclusive Lock verhindert gleichzeitige Bearbeitung, Shared Lock ermöglicht kollaborative Szenarien
  • Validierungen: Mit Prepare können Validierungen ausgeführt werden, ohne den Draft zu aktivieren

Mit dem richtigen Einsatz von Draft Handling erstellen Sie professionelle Fiori-Anwendungen, die auch bei komplexen Eingabeszenarien eine optimale User Experience bieten.

Weiterführende Themen