Developer Extensibility ermöglicht ABAP-Entwicklern, Standard-SAP-Anwendungen mit eigenem Code zu erweitern. Dieser Artikel zeigt die verschiedenen Extensibility-Konzepte und ihre praktische Anwendung.
Was ist Developer Extensibility?
Developer Extensibility ist das Erweiterungsmodell für ABAP-Entwickler in ABAP Cloud:
| Aspekt | Developer Extensibility |
|---|---|
| Zielgruppe | ABAP-Entwickler |
| Werkzeuge | ADT (Eclipse), Business Application Studio |
| Sprache | ABAP Cloud (Restricted Language Version) |
| Flexibilität | Volle Programmierfreiheit |
| Verfügbarkeit | S/4HANA Cloud, BTP ABAP Environment |
Developer vs. Key User Extensibility
| Kriterium | Developer Extensibility | Key User Extensibility |
|---|---|---|
| Komplexität | Beliebig komplex | Einfache Logik |
| Code | ABAP Cloud | Low-Code/No-Code |
| Testing | Unit Tests möglich | Manuell |
| Versionierung | Git/gCTS | Automatisch |
| Performance | Optimierbar | Vorgegeben |
| APIs | Alle Released APIs | Vordefinierte Trigger |
Extension Include Strukturen
Extension Includes ermöglichen das Hinzufügen eigener Felder zu SAP-Standardtabellen.
Konzept
SAP definiert Extension-Strukturen in Standard-Datenbanktabellen:
-- Standard-Tabelle mit Extension Includedefine table I_SALESORDER { key SalesOrder : vbeln; SalesOrderType : auart; SoldToParty : kunnr; ... -- Extension Include Point include structure ZZ1_SALESORDER_EXT;}Eigene Extension erstellen
Schritt 1: Extension Structure anlegen
@EndUserText.label: 'Sales Order Extension Fields'@AbapCatalog.enhancement.category: #EXTENSIBLE_ANYdefine structure zz1_salesorder_ext { zz1_project_id : char20; zz1_priority : /dmo/priority; zz1_external_ref : char35;}Schritt 2: In ADT aktivieren
- Rechtsklick auf die Struktur → “Include in Extension Include”
- Ziel-Tabelle auswählen (z.B. I_SALESORDER)
- Aktivieren
Beispiel: Zusatzfelder für Kundenauftrag
@EndUserText.label: 'Sales Order Customer Extensions'@AbapCatalog.enhancement.category: #EXTENSIBLE_ANYdefine structure zz1_sd_order_ext { " Projektreferenz für Auftragsverfolgung zz1_project_id : char20;
" Prioritätskennzeichen zz1_priority_code : char1;
" Externe Systemreferenz zz1_external_ref : char35;
" Kundenspezifisches Datum zz1_customer_date : dats;}Zugriff auf Extension-Felder
" In CDS Viewdefine view entity ZI_SalesOrderExt as select from I_SalesOrder{ key SalesOrder, SoldToParty,
" Extension-Felder direkt verfügbar zz1_project_id as ProjectId, zz1_priority_code as PriorityCode, zz1_external_ref as ExternalReference, zz1_customer_date as CustomerDate}BAdI-Implementierungen
BAdIs (Business Add-Ins) sind der zentrale Erweiterungsmechanismus in ABAP Cloud.
BAdI finden
In ADT nach verfügbaren BAdIs suchen:
- Open Development Object (Ctrl+Shift+A)
- Suche nach
*BADI*im gewünschten Bereich - Oder in der Dokumentation: SAP API Business Hub
Wichtige BAdI-Kategorien
| Kategorie | Beispiel | Verwendung |
|---|---|---|
| Validation | BADI_SD_SALES_DOC_CHECK | Eingabeprüfung |
| Determination | BADI_SD_PRICING | Wertableitung |
| Modification | BADI_SD_ITEM_DATA | Datenänderung |
| Authorization | BADI_AUTH_CHECK | Berechtigungen |
BAdI implementieren
Schritt 1: Enhancement Implementation anlegen
" Enhancement Implementation: ZEI_SALES_ORDER_CHECK@ObjectModel.leadingEntity.name: 'I_SALESORDER'Schritt 2: BAdI-Klasse erstellen
CLASS zcl_sales_order_validation DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_sd_sales_doc_check.
ENDCLASS.
CLASS zcl_sales_order_validation IMPLEMENTATION.
METHOD if_sd_sales_doc_check~check. " Projektnummer-Validierung LOOP AT it_order_items ASSIGNING FIELD-SYMBOL(<item>).
" Projekt muss bei bestimmten Auftragsarten gefüllt sein IF <item>-order_type = 'ZPR' AND <item>-zz1_project_id IS INITIAL.
" Fehler zur Nachrichtentabelle hinzufügen APPEND VALUE #( msgty = 'E' msgid = 'ZSD' msgno = '001' msgv1 = <item>-sales_order_item ) TO ct_messages.
ENDIF.
ENDLOOP. ENDMETHOD.
ENDCLASS.Beispiel: Preisfindungs-BAdI
CLASS zcl_custom_pricing DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_sd_pricing_exit.
ENDCLASS.
CLASS zcl_custom_pricing IMPLEMENTATION.
METHOD if_sd_pricing_exit~calculate_price. " Kundenspezifischer Rabatt basierend auf Priorität DATA(lv_priority) = is_item_data-zz1_priority_code.
CASE lv_priority. WHEN 'A'. " VIP-Kunden: 5% Extra-Rabatt cv_discount_percent = cv_discount_percent + 5.
WHEN 'B'. " Wichtige Kunden: 2.5% Extra-Rabatt cv_discount_percent = cv_discount_percent + '2.5'.
WHEN OTHERS. " Keine Änderung ENDCASE.
ENDMETHOD.
ENDCLASS.BAdI mit Filter implementieren
Filter ermöglichen gezielte Aktivierung:
CLASS zcl_sales_check_de DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_sd_sales_doc_check.
PRIVATE SECTION. " Nur für bestimmte Länder aktiv CONSTANTS: c_country_filter TYPE land1 VALUE 'DE'.
ENDCLASS.
CLASS zcl_sales_check_de IMPLEMENTATION.
METHOD if_sd_sales_doc_check~check. " Prüfung nur für deutsche Aufträge IF is_header-country <> c_country_filter. RETURN. ENDIF.
" Deutsche USt-ID Prüfung IF is_header-vat_registration IS INITIAL AND is_header-customer_type = 'B2B'.
APPEND VALUE #( msgty = 'E' msgid = 'ZSD' msgno = '002' msgv1 = 'USt-ID für B2B-Kunden erforderlich' ) TO ct_messages.
ENDIF. ENDMETHOD.
ENDCLASS.CDS View Extensions
CDS View Extensions erweitern Standard-CDS-Views um eigene Felder und Logik.
Extend View Entity
extend view entity I_SalesOrder with ZE_SalesOrder_Ext { " Extension-Felder hinzufügen zz1_project_id as ProjectId, zz1_priority_code as PriorityCode, zz1_customer_date as CustomerDate,
" Berechnetes Feld case zz1_priority_code when 'A' then 'High Priority' when 'B' then 'Medium Priority' when 'C' then 'Low Priority' else 'Not Classified' end as PriorityText,
" Assoziation zu Custom Entity _CustomProject}Mit Assoziationen erweitern
extend view entity I_SalesOrder with ZE_SalesOrder_Project { " Assoziation zu Projektdaten association [0..1] to ZI_Project as _CustomProject on $projection.zz1_project_id = _CustomProject.ProjectId,
" Felder aus der Assoziation exponieren _CustomProject.ProjectName, _CustomProject.ProjectManager, _CustomProject.PlannedEndDate}Beispiel: Kundenrating-Extension
@EndUserText.label: 'Business Partner Extension - Customer Rating'extend view entity I_BusinessPartner with ZE_BP_CustomerRating { " Extension-Felder für Kundenbewertung zz1_customer_rating as CustomerRating, zz1_last_rating_date as LastRatingDate,
" Berechneter Rating-Status case when zz1_customer_rating >= 8 then 'Gold' when zz1_customer_rating >= 5 then 'Silver' when zz1_customer_rating >= 3 then 'Bronze' else 'Standard' end as RatingCategory,
" Assoziation zu Rating-Historie association [0..*] to ZI_CustomerRatingHistory as _RatingHistory on $projection.BusinessPartner = _RatingHistory.BusinessPartner,
_RatingHistory}Access Control für Extensions
@EndUserText.label: 'Access Control for Sales Order Extension'@MappingRole: truedefine role ZE_SalesOrder_Auth { grant select on ZE_SalesOrder_Ext where ( SalesOrganization ) = aspect pfcg_auth( V_VBAK_VKO, VKORG, ACTVT = '03' ) and ( zz1_priority_code ) = aspect pfcg_auth( Z_PRIORITY, PRIORITY, ACTVT = '03' );}Behavior Extensions
Behavior Extensions erweitern das RAP-Verhalten von Standard-Entitäten.
Behavior Extension definieren
extension using interface zif_rap_sales_orderimplementation in class zbp_sales_order_ext unique;
extend behavior for I_SalesOrderTP alias SalesOrder {
// Eigene Validierungen hinzufügen validation validateProjectId on save { field zz1_project_id; }
// Eigene Determinations determination setDefaultPriority on modify { field SalesOrderType; }
// Eigene Actions action ( features : instance ) escalatePriority result [1] $self;
// Side Effects für Extension-Felder side effects { field zz1_project_id affects field ProjectName; }
}Behavior Extension implementieren
CLASS zbp_sales_order_ext DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES zif_rap_sales_order.
ENDCLASS.
CLASS zbp_sales_order_ext IMPLEMENTATION.
METHOD zif_rap_sales_order~validateProjectId. " Projekt-IDs lesen READ ENTITIES OF i_salesordertp IN LOCAL MODE ENTITY SalesOrder FIELDS ( zz1_project_id SalesOrderType ) WITH CORRESPONDING #( keys ) RESULT DATA(lt_orders).
LOOP AT lt_orders ASSIGNING FIELD-SYMBOL(<order>). " Bei Projektaufträgen muss Projekt-ID gefüllt sein IF <order>-SalesOrderType = 'ZPR' AND <order>-zz1_project_id IS INITIAL.
APPEND VALUE #( %tky = <order>-%tky %msg = new_message_with_text( severity = if_abap_behv_message=>severity-error text = 'Projektauftrag erfordert eine Projekt-ID' ) %element-zz1_project_id = if_abap_behv=>mk-on ) TO reported-salesorder.
APPEND VALUE #( %tky = <order>-%tky ) TO failed-salesorder. ENDIF. ENDLOOP. ENDMETHOD.
METHOD zif_rap_sales_order~setDefaultPriority. " Auftragstyp lesen READ ENTITIES OF i_salesordertp IN LOCAL MODE ENTITY SalesOrder FIELDS ( SalesOrderType zz1_priority_code ) WITH CORRESPONDING #( keys ) RESULT DATA(lt_orders).
" Default-Priorität basierend auf Auftragstyp LOOP AT lt_orders ASSIGNING FIELD-SYMBOL(<order>) WHERE zz1_priority_code IS INITIAL.
DATA(lv_priority) = COND char1( WHEN <order>-SalesOrderType = 'ZEXP' THEN 'A' " Express WHEN <order>-SalesOrderType = 'ZSTD' THEN 'C' " Standard ELSE 'B' " Default Medium ).
MODIFY ENTITIES OF i_salesordertp IN LOCAL MODE ENTITY SalesOrder UPDATE FIELDS ( zz1_priority_code ) WITH VALUE #( ( %tky = <order>-%tky zz1_priority_code = lv_priority ) ). ENDLOOP. ENDMETHOD.
METHOD zif_rap_sales_order~escalatePriority. " Aktuelle Daten lesen READ ENTITIES OF i_salesordertp IN LOCAL MODE ENTITY SalesOrder FIELDS ( zz1_priority_code ) WITH CORRESPONDING #( keys ) RESULT DATA(lt_orders).
LOOP AT lt_orders ASSIGNING FIELD-SYMBOL(<order>). " Priorität um eine Stufe erhöhen DATA(lv_new_priority) = COND char1( WHEN <order>-zz1_priority_code = 'C' THEN 'B' WHEN <order>-zz1_priority_code = 'B' THEN 'A' ELSE 'A' " Bereits höchste Priorität ).
MODIFY ENTITIES OF i_salesordertp IN LOCAL MODE ENTITY SalesOrder UPDATE FIELDS ( zz1_priority_code ) WITH VALUE #( ( %tky = <order>-%tky zz1_priority_code = lv_new_priority ) ).
" Ergebnis zurückgeben APPEND VALUE #( %tky = <order>-%tky %param = CORRESPONDING #( <order> ) ) TO result. ENDLOOP. ENDMETHOD.
ENDCLASS.Feature Control für Extension Actions
METHOD zif_rap_sales_order~get_instance_features. READ ENTITIES OF i_salesordertp IN LOCAL MODE ENTITY SalesOrder FIELDS ( zz1_priority_code OverallStatus ) WITH CORRESPONDING #( keys ) RESULT DATA(lt_orders).
LOOP AT lt_orders ASSIGNING FIELD-SYMBOL(<order>). " Eskalation nur wenn nicht bereits Priority A " und Auftrag nicht abgeschlossen DATA(lv_escalate_enabled) = COND #( WHEN <order>-zz1_priority_code = 'A' THEN if_abap_behv=>fc-o-disabled WHEN <order>-OverallStatus = 'C' THEN if_abap_behv=>fc-o-disabled ELSE if_abap_behv=>fc-o-enabled ).
APPEND VALUE #( %tky = <order>-%tky %action-escalatePriority = lv_escalate_enabled ) TO result. ENDLOOP.ENDMETHOD.Praktisches Beispiel: Komplette Extension
Ein vollständiges Beispiel für die Erweiterung von Kundenaufträgen:
1. Extension Include Structure
@EndUserText.label: 'Sales Order Extension Structure'@AbapCatalog.enhancement.category: #EXTENSIBLE_ANYdefine structure zz1_salesorder_ext { zz1_project_id : char20; zz1_priority : char1; zz1_approval_status : char2; zz1_approved_by : syuname; zz1_approved_at : timestampl;}2. CDS View Extension
extend view entity I_SalesOrderTP with ZE_SalesOrderTP_Ext { zz1_project_id as ProjectId, zz1_priority as Priority, zz1_approval_status as ApprovalStatus, zz1_approved_by as ApprovedBy, zz1_approved_at as ApprovedAt,
case zz1_approval_status when 'AP' then 'Approved' when 'RJ' then 'Rejected' when 'PN' then 'Pending' else 'Not Required' end as ApprovalStatusText}3. Behavior Extension
extension implementation in class zbp_so_approval_ext unique;
extend behavior for I_SalesOrderTP alias SalesOrder {
validation validateApprovalRequired on save { field NetAmount; }
determination setApprovalPending on modify { create; }
action ( features : instance ) approve; action ( features : instance ) reject;
}4. Implementation Class
CLASS zbp_so_approval_ext IMPLEMENTATION.
METHOD validateApprovalRequired. " Aufträge über 10.000 EUR müssen genehmigt werden READ ENTITIES OF i_salesordertp IN LOCAL MODE ENTITY SalesOrder FIELDS ( NetAmount zz1_approval_status TransactionCurrency ) WITH CORRESPONDING #( keys ) RESULT DATA(lt_orders).
LOOP AT lt_orders ASSIGNING FIELD-SYMBOL(<order>). IF <order>-NetAmount > 10000 AND <order>-zz1_approval_status <> 'AP'.
APPEND VALUE #( %tky = <order>-%tky %msg = new_message_with_text( severity = if_abap_behv_message=>severity-error text = |Aufträge über 10.000 { <order>-TransactionCurrency } erfordern Genehmigung| ) ) TO reported-salesorder.
APPEND VALUE #( %tky = <order>-%tky ) TO failed-salesorder. ENDIF. ENDLOOP. ENDMETHOD.
METHOD setApprovalPending. " Neue Aufträge über Schwellwert auf 'Pending' setzen READ ENTITIES OF i_salesordertp IN LOCAL MODE ENTITY SalesOrder FIELDS ( NetAmount ) WITH CORRESPONDING #( keys ) RESULT DATA(lt_orders).
LOOP AT lt_orders ASSIGNING FIELD-SYMBOL(<order>) WHERE NetAmount > 10000.
MODIFY ENTITIES OF i_salesordertp IN LOCAL MODE ENTITY SalesOrder UPDATE FIELDS ( zz1_approval_status ) WITH VALUE #( ( %tky = <order>-%tky zz1_approval_status = 'PN' " Pending ) ). ENDLOOP. ENDMETHOD.
METHOD approve. MODIFY ENTITIES OF i_salesordertp IN LOCAL MODE ENTITY SalesOrder UPDATE FIELDS ( zz1_approval_status zz1_approved_by zz1_approved_at ) WITH VALUE #( FOR key IN keys ( %tky = key-%tky zz1_approval_status = 'AP' zz1_approved_by = cl_abap_context_info=>get_user_technical_name( ) zz1_approved_at = cl_abap_context_info=>get_system_time( ) ) ).
" Ergebnis zurückgeben READ ENTITIES OF i_salesordertp IN LOCAL MODE ENTITY SalesOrder ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(lt_orders).
result = CORRESPONDING #( lt_orders ). ENDMETHOD.
METHOD reject. MODIFY ENTITIES OF i_salesordertp IN LOCAL MODE ENTITY SalesOrder UPDATE FIELDS ( zz1_approval_status zz1_approved_by zz1_approved_at ) WITH VALUE #( FOR key IN keys ( %tky = key-%tky zz1_approval_status = 'RJ' zz1_approved_by = cl_abap_context_info=>get_user_technical_name( ) zz1_approved_at = cl_abap_context_info=>get_system_time( ) ) ).
READ ENTITIES OF i_salesordertp IN LOCAL MODE ENTITY SalesOrder ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(lt_orders).
result = CORRESPONDING #( lt_orders ). ENDMETHOD.
ENDCLASS.Best Practices
Extension-Entwicklung
| Empfehlung | Begründung |
|---|---|
| Eigene Namensräume | ZZ1_, YY1_ für SAP-Cloud-Kompatibilität |
| Interface-basiert | Testbarkeit und Entkopplung |
| Dokumentation | Jede Extension dokumentieren |
| Unit Tests | Automatisierte Tests für BAdIs |
Vermeiden
❌ Direkte Tabellenänderungen ohne Extension Include❌ Hardcodierte Werte in BAdIs❌ Zu viele Extensions auf eine Entität❌ Extensions ohne Fehlerbehandlung❌ Performance-intensive Logik in DeterminationsVersionierung
Empfohlene Commit-Struktur:├── feat: Add project tracking extension fields├── feat: Implement validation BAdI for projects├── feat: Add CDS view extension with project assoc└── feat: Implement approval workflow behavior extMigration und Upgrade
Von Key User zu Developer Extensibility
- Custom Fields beibehalten: Datenstrukturen bleiben erhalten
- Custom Logic ersetzen: ABAP-BAdI statt Key User Logic
- Testen: Sicherstellen, dass beide Implementierungen nicht kollidieren
- Deaktivieren: Key User Logic nach erfolgreicher Migration deaktivieren
Upgrade-Sicherheit
- Extension Includes sind upgrade-stabil
- BAdI-Schnittstellen können sich ändern → Release Notes beachten
- CDS View Extensions bei Basisview-Änderungen prüfen
Weiterführende Themen
- Key User Extensibility - Low-Code-Erweiterungen
- Wrapper-Klassen - Classic APIs für ABAP Cloud
- RAP Feature Control - Dynamische UI-Steuerung
- RAP Authorization - Berechtigungen in RAP