La Developer Extensibility permet aux developpeurs ABAP d’etendre les applications SAP standard avec leur propre code. Cet article presente les differents concepts d’extensibilite et leur application pratique.
Qu’est-ce que la Developer Extensibility ?
La Developer Extensibility est le modele d’extension pour les developpeurs ABAP dans ABAP Cloud :
| Aspect | Developer Extensibility |
|---|---|
| Public cible | Developpeurs ABAP |
| Outils | ADT (Eclipse), Business Application Studio |
| Langage | ABAP Cloud (Restricted Language Version) |
| Flexibilite | Liberte de programmation complete |
| Disponibilite | S/4HANA Cloud, BTP ABAP Environment |
Developer vs. Key User Extensibility
| Critere | Developer Extensibility | Key User Extensibility |
|---|---|---|
| Complexite | Arbitrairement complexe | Logique simple |
| Code | ABAP Cloud | Low-Code/No-Code |
| Tests | Tests unitaires possibles | Manuel |
| Versionnement | Git/gCTS | Automatique |
| Performance | Optimisable | Predeterminee |
| APIs | Toutes les APIs Released | Triggers predefinis |
Extension Include Structures
Les Extension Includes permettent d’ajouter ses propres champs aux tables SAP standard.
Concept
SAP definit des structures d’extension dans les tables de base de donnees standard :
-- Table standard avec Extension Includedefine table I_SALESORDER { key SalesOrder : vbeln; SalesOrderType : auart; SoldToParty : kunnr; ... -- Extension Include Point include structure ZZ1_SALESORDER_EXT;}Creer sa propre Extension
Etape 1 : Creer une Extension Structure
@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;}Etape 2 : Activer dans ADT
- Clic droit sur la structure → “Include in Extension Include”
- Selectionner la table cible (par ex. I_SALESORDER)
- Activer
Exemple : Champs supplementaires pour commande client
@EndUserText.label: 'Sales Order Customer Extensions"@AbapCatalog.enhancement.category: #EXTENSIBLE_ANYdefine structure zz1_sd_order_ext { " Reference projet pour le suivi des commandes zz1_project_id : char20;
" Indicateur de priorite zz1_priority_code : char1;
" Reference systeme externe zz1_external_ref : char35;
" Date specifique au client zz1_customer_date : dats;}Acces aux champs d’extension
" Dans une CDS Viewdefine view entity ZI_SalesOrderExt as select from I_SalesOrder{ key SalesOrder, SoldToParty,
" Champs d'extension directement disponibles zz1_project_id as ProjectId, zz1_priority_code as PriorityCode, zz1_external_ref as ExternalReference, zz1_customer_date as CustomerDate}Implementations BAdI
Les BAdIs (Business Add-Ins) sont le mecanisme d’extension central dans ABAP Cloud.
Trouver un BAdI
Rechercher les BAdIs disponibles dans ADT :
- Open Development Object (Ctrl+Shift+A)
- Rechercher
*BADI*dans le domaine souhaite - Ou dans la documentation : SAP API Business Hub
Categories BAdI importantes
| Categorie | Exemple | Utilisation |
|---|---|---|
| Validation | BADI_SD_SALES_DOC_CHECK | Verification des entrees |
| Determination | BADI_SD_PRICING | Derivation de valeurs |
| Modification | BADI_SD_ITEM_DATA | Modification de donnees |
| Authorization | BADI_AUTH_CHECK | Autorisations |
Implementer un BAdI
Etape 1 : Creer une Enhancement Implementation
" Enhancement Implementation : ZEI_SALES_ORDER_CHECK@ObjectModel.leadingEntity.name: 'I_SALESORDER"Etape 2 : Creer la classe BAdI
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. " Validation du numero de projet LOOP AT it_order_items ASSIGNING FIELD-SYMBOL(<item>).
" Le projet doit etre renseigne pour certains types de commande IF <item>-order_type = 'ZPR" AND <item>-zz1_project_id IS INITIAL.
" Ajouter une erreur a la table de messages APPEND VALUE #( msgty = 'E" msgid = 'ZSD" msgno = '001" msgv1 = <item>-sales_order_item ) TO ct_messages.
ENDIF.
ENDLOOP. ENDMETHOD.
ENDCLASS.Exemple : BAdI de determination de prix
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. " Remise specifique au client basee sur la priorite DATA(lv_priority) = is_item_data-zz1_priority_code.
CASE lv_priority. WHEN 'A'. " Clients VIP : 5% de remise supplementaire cv_discount_percent = cv_discount_percent + 5.
WHEN 'B'. " Clients importants : 2.5% de remise supplementaire cv_discount_percent = cv_discount_percent + '2.5'.
WHEN OTHERS. " Pas de modification ENDCASE.
ENDMETHOD.
ENDCLASS.Implementer un BAdI avec filtre
Les filtres permettent une activation ciblee :
CLASS zcl_sales_check_fr DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_sd_sales_doc_check.
PRIVATE SECTION. " Actif uniquement pour certains pays CONSTANTS: c_country_filter TYPE land1 VALUE 'FR'.
ENDCLASS.
CLASS zcl_sales_check_fr IMPLEMENTATION.
METHOD if_sd_sales_doc_check~check. " Verification uniquement pour les commandes francaises IF is_header-country <> c_country_filter. RETURN. ENDIF.
" Verification numero TVA francais IF is_header-vat_registration IS INITIAL AND is_header-customer_type = 'B2B'.
APPEND VALUE #( msgty = 'E" msgid = 'ZSD" msgno = '002" msgv1 = 'Numero de TVA requis pour les clients B2B" ) TO ct_messages.
ENDIF. ENDMETHOD.
ENDCLASS.CDS View Extensions
Les CDS View Extensions etendent les CDS Views standard avec ses propres champs et logiques.
Extend View Entity
extend view entity I_SalesOrder with ZE_SalesOrder_Ext { " Ajouter des champs d'extension zz1_project_id as ProjectId, zz1_priority_code as PriorityCode, zz1_customer_date as CustomerDate,
" Champ calcule case zz1_priority_code when 'A' then 'Priorite haute" when 'B' then 'Priorite moyenne" when 'C' then 'Priorite basse" else 'Non classifie" end as PriorityText,
" Association vers Custom Entity _CustomProject}Etendre avec des associations
extend view entity I_SalesOrder with ZE_SalesOrder_Project { " Association vers les donnees projet association [0..1] to ZI_Project as _CustomProject on $projection.zz1_project_id = _CustomProject.ProjectId,
" Exposer des champs de l'association _CustomProject.ProjectName, _CustomProject.ProjectManager, _CustomProject.PlannedEndDate}Exemple : Extension de notation client
@EndUserText.label: 'Business Partner Extension - Customer Rating"extend view entity I_BusinessPartner with ZE_BP_CustomerRating { " Champs d'extension pour la notation client zz1_customer_rating as CustomerRating, zz1_last_rating_date as LastRatingDate,
" Statut de notation calcule case when zz1_customer_rating >= 8 then 'Or" when zz1_customer_rating >= 5 then 'Argent" when zz1_customer_rating >= 3 then 'Bronze" else 'Standard" end as RatingCategory,
" Association vers l'historique de notation association [0..*] to ZI_CustomerRatingHistory as _RatingHistory on $projection.BusinessPartner = _RatingHistory.BusinessPartner,
_RatingHistory}Access Control pour les 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
Les Behavior Extensions etendent le comportement RAP des entites standard.
Definir une Behavior Extension
extension using interface zif_rap_sales_orderimplementation in class zbp_sales_order_ext unique;
extend behavior for I_SalesOrderTP alias SalesOrder {
// Ajouter ses propres validations validation validateProjectId on save { field zz1_project_id; }
// Ses propres Determinations determination setDefaultPriority on modify { field SalesOrderType; }
// Ses propres Actions action ( features : instance ) escalatePriority result [1] $self;
// Side Effects pour les champs d'extension side effects { field zz1_project_id affects field ProjectName; }
}Implementer la Behavior Extension
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. " Lire les IDs projet 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>). " Pour les commandes projet, l'ID projet doit etre renseigne 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 = 'Une commande projet necessite un ID projet' ) %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. " Lire le type de commande READ ENTITIES OF i_salesordertp IN LOCAL MODE ENTITY SalesOrder FIELDS ( SalesOrderType zz1_priority_code ) WITH CORRESPONDING #( keys ) RESULT DATA(lt_orders).
" Priorite par defaut basee sur le type de commande 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' " Defaut 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. " Lire les donnees actuelles 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>). " Augmenter la priorite d'un niveau DATA(lv_new_priority) = COND char1( WHEN <order>-zz1_priority_code = 'C' THEN 'B" WHEN <order>-zz1_priority_code = 'B' THEN 'A" ELSE 'A' " Deja priorite maximale ).
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 ) ).
" Retourner le resultat APPEND VALUE #( %tky = <order>-%tky %param = CORRESPONDING #( <order> ) ) TO result. ENDLOOP. ENDMETHOD.
ENDCLASS.Feature Control pour les Actions d’Extension
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>). " Escalade uniquement si pas deja Priorite A " et commande non terminee 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.Exemple pratique : Extension complete
Un exemple complet pour l’extension des commandes clients :
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 'Approuve" when 'RJ' then 'Rejete" when 'PN' then 'En attente" else 'Non requis" 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. Classe d’implementation
CLASS zbp_so_approval_ext IMPLEMENTATION.
METHOD validateApprovalRequired. " Les commandes de plus de 10 000 EUR doivent etre approuvees 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 = |Les commandes de plus de 10 000 { <order>-TransactionCurrency } necessitent une approbation| ) ) TO reported-salesorder.
APPEND VALUE #( %tky = <order>-%tky ) TO failed-salesorder. ENDIF. ENDLOOP. ENDMETHOD.
METHOD setApprovalPending. " Mettre les nouvelles commandes au-dessus du seuil sur 'En attente" 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' " En attente ) ). 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( ) ) ).
" Retourner le resultat 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.Bonnes pratiques
Developpement d’extensions
| Recommandation | Justification |
|---|---|
| Espaces de noms propres | ZZ1_, YY1_ pour la compatibilite SAP Cloud |
| Base sur interfaces | Testabilite et decouplage |
| Documentation | Documenter chaque extension |
| Tests unitaires | Tests automatises pour les BAdIs |
A eviter
❌ Modifications directes de tables sans Extension Include❌ Valeurs codees en dur dans les BAdIs❌ Trop d'extensions sur une entite❌ Extensions sans gestion d'erreurs❌ Logique lourde en performance dans les DeterminationsVersionnement
Structure de commit recommandee :├── 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 et mise a niveau
De Key User a Developer Extensibility
- Conserver les Custom Fields : les structures de donnees sont conservees
- Remplacer la Custom Logic : BAdI ABAP au lieu de Key User Logic
- Tester : s’assurer que les deux implementations ne sont pas en conflit
- Desactiver : desactiver la Key User Logic apres migration reussie
Securite des mises a niveau
- Les Extension Includes sont stables aux mises a niveau
- Les interfaces BAdI peuvent changer → consulter les Release Notes
- Verifier les CDS View Extensions lors de modifications de la vue de base
Sujets connexes
- Key User Extensibility - Extensions Low-Code
- Wrapper-Klassen - Classic APIs pour ABAP Cloud
- RAP Feature Control - Controle UI dynamique
- RAP Authorization - Autorisations dans RAP