RAP Determinations und Validations: Der komplette Guide

kategorie
ABAP-Statements
Veröffentlicht
autor
Johannes

Determinations und Validations sind die beiden Mechanismen in RAP, um Geschäftslogik automatisch auszuführen. Determinations setzen Werte, Validations prüfen sie. Das richtige Verständnis des Timings (wann wird was ausgeführt) ist entscheidend für fehlerfreie RAP Business Objects.

Der Unterschied auf einen Blick

┌─────────────────────────────────────────────────────────────┐
User Action (Create/Update) │
└────────────────────┬────────────────────────────────────────┘
┌───────────────────────┐
│ DETERMINATION │ ← Setzt automatisch Werte
"Was fehlt?" │ (Status, Nummern, Defaults)
└───────────┬───────────┘
┌───────────────────────┐
│ VALIDATION │ ← Prüft Geschäftsregeln
"Ist es korrekt?" │ (Datumslogik, Pflichtfelder)
└───────────┬───────────┘
▼ (nur bei Erfolg)
┌───────────────────────┐
Save to Database
└───────────────────────┘
AspektDeterminationValidation
ZweckWerte setzen/berechnenWerte prüfen
Timingon modify oder on saveon save
Änderungen✅ Darf Entity ändern❌ Nur lesen, keine Änderungen
FehlerKeine direkten FehlerFüllt FAILED & REPORTED
BeispielStatus auf ‘O’ setzenEnddatum nach Beginn prüfen
ReihenfolgeZuerst (vor Validation)Danach (nach Determination)

Determinations: Automatische Wertsetzung

Wann Determinations nutzen?

Perfekt für:

  • Default-Werte setzen (Status = ‘Open’)
  • Felder berechnen (Gesamtpreis = Preis × Menge)
  • Abhängige Felder aktualisieren
  • Timestamps setzen (CreatedAt, LastChangedAt)
  • Geschäftslogik-basierte Werte (Rabatt nach Kundenkategorie)

Syntax in Behavior Definition

define behavior for ZI_Travel alias Travel
{
// Determination Syntax:
determination <MethodName> on <Trigger> { <TriggerCondition>; }
// Trigger: modify (sofort) oder save (vor DB-Commit)
// TriggerCondition: create, update, field <FieldName>
}

Beispiel 1: Status bei Create setzen

// Behavior Definition
define behavior for ZI_Travel alias Travel
persistent table ztravel
{
create;
update;
field ( readonly ) TravelId, Status;
field ( readonly ) CreatedBy, CreatedAt;
// Determination: Bei Create → Status auf 'O' (Open) setzen
determination setInitialStatus on modify { create; }
}
// Behavior Implementation
CLASS lhc_travel DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
METHODS setInitialStatus FOR DETERMINE ON MODIFY
IMPORTING keys FOR Travel~setInitialStatus.
ENDCLASS.
CLASS lhc_travel IMPLEMENTATION.
METHOD setInitialStatus.
" 1. Aktuelle Daten lesen
READ ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
FIELDS ( Status )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_travel).
" 2. Nur Entities ohne Status aktualisieren
MODIFY ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
UPDATE FIELDS ( Status CreatedBy CreatedAt )
WITH VALUE #( FOR travel IN lt_travel WHERE ( Status IS INITIAL )
( %tky = travel-%tky
Status = 'O' " Open
CreatedBy = cl_abap_context_info=>get_user_name( )
CreatedAt = cl_abap_context_info=>get_system_date( ) ) )
REPORTED DATA(update_reported).
ENDMETHOD.
ENDCLASS.

Ablauf:

User: CREATE Travel mit Description = 'Geschäftsreise'
Framework: Create in Memory
Determination: setInitialStatus
→ Status = 'O'
→ CreatedBy = sy-uname
→ CreatedAt = today
(Validations werden ausgeführt)
Framework: INSERT INTO ztravel

Beispiel 2: Gesamtpreis berechnen (on modify)

// Behavior Definition
define behavior for ZI_Travel alias Travel
{
// Bei Änderung von BeginDate oder EndDate → Preis neu berechnen
determination calculateTotalPrice on modify {
field BeginDate, EndDate;
}
}
METHOD calculateTotalPrice.
" Daten lesen
READ ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
FIELDS ( TravelId BeginDate EndDate )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_travel).
LOOP AT lt_travel INTO DATA(ls_travel).
" Anzahl Tage berechnen
DATA(lv_days) = ls_travel-EndDate - ls_travel-BeginDate + 1.
" Basis-Preis pro Tag (z.B. aus Customizing)
DATA(lv_price_per_day) = 100. " EUR
" Gesamtpreis
DATA(lv_total) = lv_days * lv_price_per_day.
" Rabatt für längere Reisen
IF lv_days > 7.
lv_total = lv_total * '0.90'. " 10% Rabatt
ENDIF.
" Wert setzen
MODIFY ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
UPDATE FIELDS ( TotalPrice )
WITH VALUE #( ( %tky = ls_travel-%tky
TotalPrice = lv_total ) ).
ENDLOOP.
ENDMETHOD.

Trigger-Beispiele:

" Bei CREATE ausführen
determination setDefaults on modify { create; }
" Bei UPDATE ausführen
determination recalculate on modify { update; }
" Bei CREATE und UPDATE
determination calculate on modify { create; update; }
" Nur wenn bestimmte Felder geändert werden
determination updateDependentFields on modify {
field Price, Quantity, Discount;
}
" Bei SAVE (kurz vor DB-Commit)
determination finalizeData on save { create; update; }

Beispiel 3: Abhängige Felder setzen (on save)

// Behavior Definition
define behavior for ZI_Travel alias Travel
{
// Kurz vor Save: Approval-Felder setzen
determination setApprovalData on save { update; }
}
METHOD setApprovalData.
READ ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
FIELDS ( Status ApprovedBy ApprovedAt )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_travel).
MODIFY ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
UPDATE FIELDS ( ApprovedBy ApprovedAt )
WITH VALUE #( FOR travel IN lt_travel
WHERE ( Status = 'A' AND ApprovedBy IS INITIAL )
( %tky = travel-%tky
ApprovedBy = cl_abap_context_info=>get_user_name( )
ApprovedAt = cl_abap_context_info=>get_system_date( ) ) )
REPORTED DATA(update_reported).
ENDMETHOD.

Validations: Geschäftsregeln prüfen

Wann Validations nutzen?

Perfekt für:

  • Pflichtfelder prüfen
  • Datumslogik (Enddatum nach Beginn)
  • Wertebereichs-Checks (Rabatt ≤ 100%)
  • Fremdschlüssel prüfen (Kunde existiert?)
  • Komplexe Geschäftsregeln

NICHT für:

  • Werte setzen (→ Determination!)
  • Berechnungen (→ Determination!)
  • Nur Warnings loggen (→ OK, aber nicht als Fehler)

Syntax in Behavior Definition

define behavior for ZI_Travel alias Travel
{
// Validation Syntax:
validation <MethodName> on save { <TriggerCondition>; }
// Trigger: IMMER "on save" (kurz vor DB-Commit)
// TriggerCondition: create, update, field <FieldName>
}

Beispiel 1: Datumslogik prüfen

// Behavior Definition
define behavior for ZI_Travel alias Travel
{
create;
update;
// Validation: Bei Save → Datum prüfen
validation validateDates on save {
field BeginDate, EndDate;
}
}
// Behavior Implementation
CLASS lhc_travel IMPLEMENTATION.
METHOD validateDates.
" 1. Daten lesen
READ ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
FIELDS ( TravelId BeginDate EndDate )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_travel).
" 2. Jede Entity prüfen
LOOP AT lt_travel INTO DATA(ls_travel).
" Regel 1: EndDate muss nach BeginDate liegen
IF ls_travel-EndDate < ls_travel-BeginDate.
" FAILED füllen (technischer Fehler)
APPEND VALUE #(
%tky = ls_travel-%tky
%element-EndDate = if_abap_behv=>mk-on
) TO failed-travel.
" REPORTED füllen (Fehlermeldung für UI)
APPEND VALUE #(
%tky = ls_travel-%tky
%element-EndDate = if_abap_behv=>mk-on
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = |Enddatum ({ ls_travel-EndDate DATE = USER }) | &&
|muss nach Beginndatum ({ ls_travel-BeginDate DATE = USER }) liegen|
)
) TO reported-travel.
ENDIF.
" Regel 2: BeginDate darf nicht in Vergangenheit liegen
IF ls_travel-BeginDate < cl_abap_context_info=>get_system_date( ).
APPEND VALUE #(
%tky = ls_travel-%tky
%element-BeginDate = if_abap_behv=>mk-on
) TO failed-travel.
APPEND VALUE #(
%tky = ls_travel-%tky
%element-BeginDate = if_abap_behv=>mk-on
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'Reisebeginn darf nicht in der Vergangenheit liegen'
)
) TO reported-travel.
ENDIF.
ENDLOOP.
ENDMETHOD.
ENDCLASS.

Was passiert bei Fehler?

User: UPDATE Travel, EndDate = '20250101', BeginDate = '20250115'
(Determinations werden ausgeführt)
Validation: validateDates
→ Prüfung: EndDate < BeginDate? → JA!
→ FAILED-travel gefüllt
→ REPORTED-travel gefüllt
Framework: ROLLBACK (kein INSERT/UPDATE)
UI: Fehlermeldung anzeigen

Beispiel 2: Fremdschlüssel prüfen

// Behavior Definition
define behavior for ZI_Travel alias Travel
{
validation validateCustomer on save {
field CustomerId;
}
}
METHOD validateCustomer.
READ ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
FIELDS ( CustomerId )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_travel).
" Alle CustomerIds sammeln
DATA(lt_customer_ids) = VALUE string_table(
FOR travel IN lt_travel ( travel-CustomerId )
).
" Prüfen ob Kunden existieren (via Released API)
SELECT Customer
FROM I_Customer
FOR ALL ENTRIES IN @lt_customer_ids
WHERE Customer = @lt_customer_ids-table_line
INTO TABLE @DATA(lt_valid_customers).
" Fehler für nicht-existierende Kunden
LOOP AT lt_travel INTO DATA(ls_travel).
IF NOT line_exists( lt_valid_customers[ table_line = ls_travel-CustomerId ] ).
APPEND VALUE #(
%tky = ls_travel-%tky
%element-CustomerId = if_abap_behv=>mk-on
) TO failed-travel.
APPEND VALUE #(
%tky = ls_travel-%tky
%element-CustomerId = if_abap_behv=>mk-on
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = |Kunde { ls_travel-CustomerId } existiert nicht|
)
) TO reported-travel.
ENDIF.
ENDLOOP.
ENDMETHOD.

Beispiel 3: Komplexe Geschäftsregel

// Validation: Rabatt nur für Premium-Kunden
validation validateDiscount on save {
field Discount, CustomerId;
}
METHOD validateDiscount.
READ ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
FIELDS ( CustomerId Discount TotalPrice )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_travel).
" Kundenkategorien laden
SELECT Customer, CustomerClassification
FROM I_Customer
FOR ALL ENTRIES IN @lt_travel
WHERE Customer = @lt_travel-CustomerId
INTO TABLE @DATA(lt_customers).
LOOP AT lt_travel INTO DATA(ls_travel).
" Regel: > 10% Rabatt nur für Premium-Kunden (Kategorie 'A')
IF ls_travel-Discount > 10.
DATA(ls_customer) = VALUE #( lt_customers[ Customer = ls_travel-CustomerId ] OPTIONAL ).
IF ls_customer-CustomerClassification <> 'A'.
APPEND VALUE #(
%tky = ls_travel-%tky
%element-Discount = if_abap_behv=>mk-on
) TO failed-travel.
APPEND VALUE #(
%tky = ls_travel-%tky
%element-Discount = if_abap_behv=>mk-on
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = |Rabatt über 10% nur für Premium-Kunden (aktuell: { ls_customer-CustomerClassification })|
)
) TO reported-travel.
ENDIF.
ENDIF.
" Regel 2: Rabatt max. 50% des Gesamtpreises
IF ls_travel-Discount > ( ls_travel-TotalPrice / 2 ).
APPEND VALUE #(
%tky = ls_travel-%tky
%element-Discount = if_abap_behv=>mk-on
) TO failed-travel.
APPEND VALUE #(
%tky = ls_travel-%tky
%element-Discount = if_abap_behv=>mk-on
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'Rabatt darf maximal 50% des Gesamtpreises betragen'
)
) TO reported-travel.
ENDIF.
ENDLOOP.
ENDMETHOD.

Timing: on modify vs on save

on modify (nur Determination!)

determination calculate on modify { create; update; }

Wann ausgeführt:

  • ✅ Sofort nach CREATE/UPDATE Operation
  • ✅ BEVOR Validation läuft
  • ✅ Mehrfach möglich (bei jedem Modify)

Use Cases:

  • Default-Werte sofort setzen
  • Felder berechnen die in Validation geprüft werden
  • User Feedback (Feld wird sofort gefüllt im UI)

on save (Determination & Validation!)

determination finalize on save { update; }
validation validateData on save { field Status; }

Wann ausgeführt:

  • ✅ Kurz vor DB-Commit
  • ✅ NACH allen on modify Determinations
  • ✅ Nur 1x pro Save-Zyklus

Use Cases Determination:

  • Finale Berechnungen (z.B. Checksummen)
  • Audit-Felder (LastChangedBy, LastChangedAt)
  • Nummernvergabe aus DB-Nummernkreisen

Use Cases Validation:

  • Geschäftsregeln prüfen
  • Konsistenz sicherstellen
  • Fremdschlüssel validieren

Ausführungsreihenfolge

User Action: CREATE/UPDATE
┌───────────────────────────────────────┐
│ 1. DETERMINATION on modify (create) │ ← Sofort
└───────────────┬───────────────────────┘
┌───────────────────────────────────────┐
│ 2. DETERMINATION on modify (update) │ ← Bei jedem Update
└───────────────┬───────────────────────┘
┌───────────────────────────────────────┐
│ User: COMMIT ENTITIES aufrufen │
└───────────────┬───────────────────────┘
┌───────────────────────────────────────┐
│ 3. DETERMINATION on save │ ← Kurz vor DB
└───────────────┬───────────────────────┘
┌───────────────────────────────────────┐
│ 4. VALIDATION on save │ ← Prüfungen
└───────────────┬───────────────────────┘
├─ Fehler? → ROLLBACK, User sieht Fehlermeldung
▼ Erfolg
┌───────────────────────────────────────┐
│ 5. INSERT/UPDATE in Datenbank │ ← Persistent!
└───────────────────────────────────────┘

Beispiel mit beiden:

define behavior for ZI_Travel alias Travel
{
create;
update;
// Schritt 1: Sofort Status setzen
determination setInitialStatus on modify { create; }
// Schritt 2: Bei Feld-Änderung neu berechnen
determination calculateTotal on modify { field BeginDate, EndDate; }
// Schritt 3: Vor Save finale Werte
determination setApprovalData on save { update; }
// Schritt 4: Validierungen
validation validateDates on save { field BeginDate, EndDate; }
validation validateCustomer on save { field CustomerId; }
}

Fehlermeldungen richtig gestalten

Severity Levels

" ERROR: Blockiert Save
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'Enddatum muss nach Beginndatum liegen'
)
" WARNING: Erlaubt Save, zeigt Warnung
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-warning
text = 'Reise ist sehr kurz (< 3 Tage)'
)
" INFO: Nur Information
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-information
text = 'Rabatt wurde automatisch angewendet'
)
" SUCCESS: Positive Bestätigung
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-success
text = 'Reise erfolgreich genehmigt'
)

Message mit Message Class

" Statt Hardcoded-Text → Message Class nutzen
APPEND VALUE #(
%tky = ls_travel-%tky
%element-EndDate = if_abap_behv=>mk-on
%msg = new_message(
id = 'ZTRAVEL_MSG' " Message Class (SE91)
number = '001' " Message Number
severity = if_abap_behv_message=>severity-error
v1 = ls_travel-BeginDate " Platzhalter &1
v2 = ls_travel-EndDate " Platzhalter &2
)
) TO reported-travel.
" In SE91: Message ZTRAVEL_MSG/001
" Text: "Enddatum &2 muss nach Beginndatum &1 liegen"

Mehrere Felder markieren

" Fehler betrifft BeginDate UND EndDate
APPEND VALUE #(
%tky = ls_travel-%tky
%element-BeginDate = if_abap_behv=>mk-on
%element-EndDate = if_abap_behv=>mk-on
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'Reisezeitraum ungültig'
)
) TO reported-travel.
" UI markiert BEIDE Felder rot

Best Practices

✅ DO: Determinations

" ✅ Werte setzen, nicht prüfen
METHOD setDefaults.
MODIFY ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
UPDATE FIELDS ( Status Currency )
WITH VALUE #( FOR key IN keys
( %tky = key-%tky
Status = 'O'
Currency = 'EUR' ) ).
ENDMETHOD.
" ✅ on modify für User Feedback
determination calculatePrice on modify { field Quantity, UnitPrice; }
" → User sieht sofort neuen Preis
" ✅ on save für finale Werte
determination generateDocumentNumber on save { create; }
" → Nummer erst bei finalem Save vergeben

✅ DO: Validations

" ✅ Nur prüfen, nicht ändern
METHOD validateAmount.
READ ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel FIELDS ( Amount ) WITH CORRESPONDING #( keys )
RESULT DATA(lt_travel).
LOOP AT lt_travel INTO DATA(ls_travel) WHERE Amount <= 0.
APPEND VALUE #( %tky = ls_travel-%tky ) TO failed-travel.
APPEND VALUE #(
%tky = ls_travel-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'Betrag muss größer als 0 sein'
)
) TO reported-travel.
ENDLOOP.
ENDMETHOD.
" ✅ Performance: Bulk-Read statt Loop
SELECT Customer FROM I_Customer
FOR ALL ENTRIES IN @lt_travel
WHERE Customer = @lt_travel-CustomerId
INTO TABLE @DATA(lt_valid).
" ✅ Sprechende Fehlermeldungen
text = |Kunde { ls_travel-CustomerId } existiert nicht im System|
" Statt: "Validierung fehlgeschlagen"

❌ DON’T

" ❌ NICHT in Validation ändern
METHOD validateData.
" FALSCH: Validation darf NICHT modifizieren!
MODIFY ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel UPDATE FIELDS ( Status ) WITH ...
" → Kann zu Inkonsistenzen führen!
ENDMETHOD.
" ❌ NICHT in Determination prüfen und abbrechen
METHOD setDefaults.
IF ls_travel-Amount <= 0.
" FALSCH: Determination soll nicht validieren!
APPEND ... TO failed-travel. " ❌ Nutze Validation!
ENDIF.
ENDMETHOD.
" ❌ NICHT on modify für DB-Zugriffe
determination getCustomerData on modify { field CustomerId; }
" → Jedes Mal wenn Feld ändert = DB-Call
" → Besser: on save (nur 1x vor Commit)
" ❌ NICHT generische Fehlermeldungen
text = 'Fehler' " ❌ Nicht hilfreich
text = 'Validation failed' " ❌ Was ist das Problem?
" → Konkret: "Enddatum muss nach Beginndatum liegen"

Testing

CLASS ltc_determinations DEFINITION FINAL FOR TESTING
DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
DATA mo_env TYPE REF TO if_cds_test_environment.
METHODS:
setup,
teardown,
test_set_initial_status FOR TESTING,
test_calculate_total FOR TESTING.
ENDCLASS.
CLASS ltc_determinations IMPLEMENTATION.
METHOD setup.
mo_env = cl_cds_test_environment=>create( i_for_entity = 'ZI_Travel' ).
ENDMETHOD.
METHOD test_set_initial_status.
" Arrange: Travel ohne Status erstellen
MODIFY ENTITIES OF zi_travel
ENTITY Travel
CREATE FIELDS ( AgencyId CustomerId )
WITH VALUE #( ( %cid = 'T1' AgencyId = '001' CustomerId = '042' ) )
MAPPED DATA(mapped).
COMMIT ENTITIES.
" Act: Determination sollte Status gesetzt haben
READ ENTITIES OF zi_travel
ENTITY Travel FIELDS ( Status )
WITH VALUE #( ( %cid = 'T1' ) )
RESULT DATA(lt_travel).
" Assert: Status = 'O'
cl_abap_unit_assert=>assert_equals(
exp = 'O'
act = lt_travel[ 1 ]-Status
msg = 'Determination sollte Status auf O setzen'
).
ENDMETHOD.
METHOD test_calculate_total.
" Test für calculation determination
" ... (analog zu oben)
ENDMETHOD.
METHOD teardown.
ROLLBACK ENTITIES.
mo_env->destroy( ).
ENDMETHOD.
ENDCLASS.
CLASS ltc_validations DEFINITION FINAL FOR TESTING
DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
DATA mo_env TYPE REF TO if_cds_test_environment.
METHODS:
setup,
test_validate_dates_error FOR TESTING,
test_validate_dates_success FOR TESTING.
ENDCLASS.
CLASS ltc_validations IMPLEMENTATION.
METHOD setup.
mo_env = cl_cds_test_environment=>create( i_for_entity = 'ZI_Travel' ).
ENDMETHOD.
METHOD test_validate_dates_error.
" Arrange: EndDate < BeginDate
MODIFY ENTITIES OF zi_travel
ENTITY Travel
CREATE FIELDS ( BeginDate EndDate )
WITH VALUE #( ( %cid = 'T1'
BeginDate = '20250615'
EndDate = '20250601' ) ) " FALSCH!
FAILED DATA(failed).
COMMIT ENTITIES
RESPONSE OF zi_travel
FAILED DATA(commit_failed)
REPORTED DATA(commit_reported).
" Assert: Fehler erwartet
cl_abap_unit_assert=>assert_not_initial(
act = commit_failed-travel
msg = 'Validation sollte Fehler melden'
).
" Message prüfen
cl_abap_unit_assert=>assert_bound(
act = commit_reported-travel[ 1 ]-%msg
msg = 'Fehlermeldung sollte vorhanden sein'
).
ENDMETHOD.
METHOD test_validate_dates_success.
" Arrange: Korrekte Daten
MODIFY ENTITIES OF zi_travel
ENTITY Travel
CREATE FIELDS ( BeginDate EndDate )
WITH VALUE #( ( %cid = 'T1'
BeginDate = '20250601'
EndDate = '20250615' ) )
FAILED DATA(failed).
COMMIT ENTITIES
RESPONSE OF zi_travel
FAILED DATA(commit_failed).
" Assert: Kein Fehler
cl_abap_unit_assert=>assert_initial(
act = commit_failed-travel
msg = 'Validation sollte erfolgreich sein'
).
ENDMETHOD.
ENDCLASS.

Wichtige Hinweise / Best Practice

  • Determination = Setzen, Validation = Prüfen: Niemals vermischen!
  • Timing beachten: on modify für sofortiges Feedback, on save für finale Operationen
  • Validation darf NICHT ändern: Nur lesen und FAILED/REPORTED füllen
  • Performance: Bulk-Operationen statt Loops mit einzelnen DB-Calls
  • Sprechende Messages: Konkret beschreiben, was falsch ist
  • %element markieren: Zeigt User genau welches Feld das Problem hat
  • IN LOCAL MODE: Immer in Behavior Implementations verwenden
  • Severity richtig: ERROR blockiert, WARNING erlaubt Save
  • Message Class nutzen: Statt Hardcoded-Texte → Übersetzbarkeit
  • Reihenfolge: Determinations → Validations → DB Save
  • Testen: Unit Tests für JEDE Determination und Validation schreiben
  • Fehler sammeln: Alle Fehler auf einmal melden, nicht beim ersten abbrechen

Weitere Ressourcen